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 stat 27import subprocess 28import sys 29import time 30 31 32def get_env_path_from_rsp(script_file: str) -> list: 33 """get env path from response file recursively.""" 34 rsp_file = "{0}{1}".format(script_file, ".rsp") 35 if not os.path.exists(rsp_file): 36 print( 37 "File \"{}\" does not exist!\n" \ 38 "This indicates that its related shared_library is not compiled by this project, but there is an " \ 39 "executable or shared_library depend on its related shared_library!".format(rsp_file)) 40 sys.exit(1) 41 42 rsp_info_list = [] 43 with open(rsp_file, "r") as fi: 44 rsp_info_str = fi.read() 45 rsp_info_list = rsp_info_str.split(" ") 46 47 env_path_list = [] 48 for element in rsp_info_list: 49 if element.endswith(".so") or element.endswith(".dll"): 50 env_path_list.extend(get_env_path_from_rsp(element)) 51 env_path_list.append(os.path.dirname(element)) 52 return env_path_list 53 54 55def get_command_and_env_path(args: object) -> [str, str]: 56 """get command and environment path from args for running excutable.""" 57 env_path_list = list(set(get_env_path_from_rsp(args.script_file))) 58 env_path_list.append(args.clang_lib_path) 59 env_path = ":".join(env_path_list) 60 if args.qemu_binary_path: 61 if not os.path.exists(args.qemu_binary_path): 62 print("Have you set up environment for running executables with qemu?\n" \ 63 "If not, get set-up steps from https://gitee.com/ark_standalone_build/docs ," \ 64 " append your build command of ark.py with option \"--clean-continue\"," \ 65 " and execute the appended command after setting up the environment.\n" \ 66 "If yes, the environment settings for qemu on your host machine may be different from what the link" \ 67 " above shows, it is suggested to match your local environment settings with what the link shows.") 68 sys.exit(1) 69 cmd = \ 70 "{}".format(args.qemu_binary_path) + \ 71 " -L {}".format(args.qemu_ld_prefix) + \ 72 " -E LD_LIBRARY_PATH={}".format(env_path) + \ 73 " {}".format(args.script_file) 74 else: 75 cmd = "{}".format(args.script_file) 76 cmd += " {}".format(args.script_options) if args.script_options else "" 77 cmd += " {}".format(args.script_args) if args.script_args else "" 78 return [cmd, env_path] 79 80 81def parse_args() -> object: 82 """parse arguments.""" 83 parser = argparse.ArgumentParser() 84 parser.add_argument('--script-file', help='execute script file') 85 parser.add_argument('--script-options', help='execute script options') 86 parser.add_argument('--script-args', help='args of script') 87 parser.add_argument('--expect-output', help='expect output') 88 parser.add_argument('--expect-sub-output', help='expect sub output') 89 parser.add_argument('--expect-file', help='expect file') 90 parser.add_argument('--env-path', help='LD_LIBRARY_PATH env') 91 parser.add_argument('--timeout-limit', help='timeout limit') 92 parser.add_argument('--clang-lib-path', help='part for LD_LIBRARY_PATH, it is not in .rsp file') 93 parser.add_argument('--qemu-binary-path', help='path to qemu binary, run executable with qemu if assigned') 94 parser.add_argument('--qemu-ld-prefix', help='elf interpreter prefix') 95 args = parser.parse_args() 96 return args 97 98 99def process_open(args: object) -> [str, object]: 100 """get command and open subprocess.""" 101 if args.env_path: 102 # use the given env-path 103 cmd = args.script_file 104 cmd += " {}".format(args.script_options) if args.script_options else "" 105 cmd += " {}".format(args.script_args) if args.script_args else "" 106 # process for running executable directly 107 subp = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, 108 env={'LD_LIBRARY_PATH': str(args.env_path)}) 109 else: 110 # get env-path from response file recursively 111 [cmd, env_path] = get_command_and_env_path(args) 112 if args.qemu_binary_path: 113 # process for running executable with qemu 114 subp = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 115 else: 116 # process for running executable directly 117 subp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, 118 env={'LD_LIBRARY_PATH': str(env_path)}) 119 return [cmd, subp] 120 121 122def generate_stub_code_comment(out_str:str): 123 flags = os.O_WRONLY | os.O_CREAT 124 mode = stat.S_IWUSR | stat.S_IRUSR 125 dir_path = './gen/arkcompiler/ets_runtime/' 126 if not os.path.exists(dir_path): 127 return 128 file_path = dir_path + 'stub_code_comment.txt' 129 fd = os.open(file_path, flags, mode) 130 with os.fdopen(fd, "w") as code_comment_file: 131 code_comment_file.write(out_str) 132 133 134def judge_output(args: object): 135 """run executable and judge is success or not.""" 136 start_time = time.time() 137 [cmd, subp] = process_open(args) 138 timeout_limit = int(args.timeout_limit) if args.timeout_limit else 1200 # units: s 139 140 try: 141 out, err = subp.communicate(timeout=timeout_limit) 142 except subprocess.TimeoutExpired: 143 raise RuntimeError('Run [', cmd, '] timeout, timeout_limit = ', timeout_limit, 's') 144 145 out_str = out.decode('UTF-8', errors="ignore") 146 err_str = err.decode('UTF-8', errors="ignore") 147 generate_stub_code_comment(out_str) 148 returncode = str(subp.returncode) 149 if args.expect_output: 150 if returncode != args.expect_output: 151 print(">>>>> ret <<<<<") 152 print(returncode) 153 print(">>>>> out <<<<<") 154 print(out_str) 155 print(">>>>> err <<<<<") 156 print(err_str) 157 print(">>>>> Expect return: [" + args.expect_output \ 158 + "]\n>>>>> But got: [" + returncode + "]") 159 raise RuntimeError("Run [" + cmd + "] failed!") 160 elif args.expect_sub_output: 161 if out_str.find(args.expect_sub_output) == -1 or returncode != "0": 162 print(">>>>> ret <<<<<") 163 print(returncode) 164 print(">>>>> err <<<<<") 165 print(err_str) 166 print(">>>>> Expect contain: [" + args.expect_sub_output \ 167 + "]\n>>>>> But got: [" + out_str + "]") 168 raise RuntimeError("Run [" + cmd + "] failed!") 169 elif args.expect_file: 170 with open(args.expect_file, mode='r') as file: 171 # skip license header 172 expect_output = ''.join(file.readlines()[13:]) 173 file.close() 174 result_cmp = compare_line_by_line(expect_output, out_str) 175 if result_cmp or returncode != "0": 176 print(">>>>> ret <<<<<") 177 print(returncode) 178 print(">>>>> err <<<<<") 179 print(err_str) 180 print(">>>>> Expect {} lines: [{}]\n>>>>> But got {} lines: [{}]".format( 181 expect_output.count('\n'), expect_output, out_str.count('\n'), out_str 182 )) 183 raise RuntimeError("Run [" + cmd + "] failed!") 184 else: 185 raise RuntimeError("Run [" + cmd + "] with no expect !") 186 187 print("Run [" + cmd + "] success!") 188 print("used: %.5f seconds" % (time.time() - start_time)) 189 190def compare_line_by_line(expect_output:str, got_output:str): 191 expect_output_list = expect_output.split("\n") 192 got_output_list = got_output.split("\n") 193 for index, (expect_line, got_line) in enumerate(zip(expect_output_list, got_output_list)): 194 if expect_line == got_line: 195 continue 196 error_msg = "" 197 198 if "__INT_MORE_PREV__" in expect_line: 199 prev_got_value = reverse_find_first_not_trace_line(got_output_list, index-1) 200 if got_line.isdigit() and prev_got_value.isdigit() and int(prev_got_value) < int(got_line): 201 continue 202 error_msg = "Got integer result is not more than previous integer result" 203 204 if "__INT__" in expect_line: 205 if got_line.isdigit(): 206 continue 207 error_msg = "Got not integer" 208 209 print(">>>>> diff <<<<<") 210 if error_msg: 211 print(error_msg) 212 print("Difference in line {}:\nExcepted: [{}]\nBut got: [{}]".format(index+1, expect_line, got_line)) 213 return True 214 return False 215 216def reverse_find_first_not_trace_line(output_list: list, init_index: int) -> str: 217 for i in range(init_index, -1, -1): 218 if "[trace]" not in output_list[i]: 219 return output_list[i] 220 return "" 221 222if __name__ == '__main__': 223 input_args = parse_args() 224 judge_output(input_args) 225