feat(tools): Add test tool and std.c
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
/target
|
/target
|
||||||
/testcases
|
/testcases
|
||||||
/output
|
/output
|
||||||
|
*.pyc
|
||||||
|
*.zip
|
||||||
+121
@@ -0,0 +1,121 @@
|
|||||||
|
///
|
||||||
|
/// @file std.c
|
||||||
|
/// @brief 外部或内置函数实现
|
||||||
|
/// @author zenglj (zenglj@live.com)
|
||||||
|
/// @version 1.0
|
||||||
|
/// @date 2024-09-29
|
||||||
|
///
|
||||||
|
/// @copyright Copyright (c) 2024
|
||||||
|
///
|
||||||
|
/// @par 修改日志:
|
||||||
|
/// <table>
|
||||||
|
/// <tr><th>Date <th>Version <th>Author <th>Description
|
||||||
|
/// <tr><td>2024-09-29 <td>1.0 <td>zenglj <td>新做
|
||||||
|
/// </table>
|
||||||
|
///
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
int getint()
|
||||||
|
{
|
||||||
|
int d;
|
||||||
|
|
||||||
|
scanf("%d", &d);
|
||||||
|
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getch()
|
||||||
|
{
|
||||||
|
char d;
|
||||||
|
|
||||||
|
scanf("%c", &d);
|
||||||
|
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getarray(int a[])
|
||||||
|
{
|
||||||
|
int n, i;
|
||||||
|
|
||||||
|
// 获取元素个数
|
||||||
|
scanf("%d",&n);
|
||||||
|
|
||||||
|
// 获取元素内容
|
||||||
|
for(i = 0; i < n; ++i) {
|
||||||
|
scanf("%d",&a[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void putint(int k)
|
||||||
|
{
|
||||||
|
printf("%d", k);
|
||||||
|
}
|
||||||
|
|
||||||
|
void putch(int c)
|
||||||
|
{
|
||||||
|
printf("%c", (char)c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void putarray(int n, int * d)
|
||||||
|
{
|
||||||
|
int k;
|
||||||
|
|
||||||
|
// 输出元素个数
|
||||||
|
printf("%d:", n);
|
||||||
|
|
||||||
|
// 输出元素内容,空格分割
|
||||||
|
for(k = 0; k < n; k ++) {
|
||||||
|
printf(" %d", d[k]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 输出换行符
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void putstr(char * str)
|
||||||
|
{
|
||||||
|
printf("%s", str);
|
||||||
|
}
|
||||||
|
|
||||||
|
float getfloat()
|
||||||
|
{
|
||||||
|
float n;
|
||||||
|
scanf("%a", &n);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getfarray(float a[])
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
scanf("%d", &n);
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
scanf("%a", &a[i]);
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void putfloat(float a)
|
||||||
|
{
|
||||||
|
printf("%a", a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void putfarray(int n, float a[])
|
||||||
|
{
|
||||||
|
printf("%d:", n);
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
printf(" %a", a[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void putf(char a[], ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, a);
|
||||||
|
vfprintf(stdout, a, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
Executable
+177
@@ -0,0 +1,177 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Compile an ARM assembly file and run it with qemu-arm-static."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args() -> argparse.Namespace:
|
||||||
|
if "--" in sys.argv:
|
||||||
|
separator_index = sys.argv.index("--")
|
||||||
|
tool_args = sys.argv[1:separator_index]
|
||||||
|
program_args = sys.argv[separator_index + 1 :]
|
||||||
|
else:
|
||||||
|
tool_args = sys.argv[1:]
|
||||||
|
program_args = []
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description=(
|
||||||
|
"Compile an ARM assembly file with arm-linux-gnueabihf-gcc, "
|
||||||
|
"run it with qemu-arm-static, then print stdout and $?."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
parser.add_argument("asm_file", type=Path, help="path to the assembly file")
|
||||||
|
parser.add_argument(
|
||||||
|
"--cc",
|
||||||
|
default="arm-linux-gnueabihf-gcc",
|
||||||
|
help="cross compiler to use (default: arm-linux-gnueabihf-gcc)",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--qemu",
|
||||||
|
default="qemu-arm-static",
|
||||||
|
help="ARM qemu runner to use (default: qemu-arm-static)",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-o",
|
||||||
|
"--output",
|
||||||
|
type=Path,
|
||||||
|
help="output executable path; defaults to an auto-cleaned temporary file",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-g",
|
||||||
|
"--gdb",
|
||||||
|
action="store_true",
|
||||||
|
help="compile with -g and run qemu in gdb stub mode",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--gdb-port",
|
||||||
|
default="1234",
|
||||||
|
metavar="PORT",
|
||||||
|
help="qemu gdb stub port used with -g/--gdb (default: 1234)",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--gdb-command",
|
||||||
|
help=(
|
||||||
|
"gdb command shown in debug instructions "
|
||||||
|
"(default: first available of arm-linux-gnueabihf-gdb, gdb-multiarch, gdb)"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--no-static",
|
||||||
|
action="store_true",
|
||||||
|
help="do not pass -static to the compiler",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--gcc-arg",
|
||||||
|
action="append",
|
||||||
|
default=[],
|
||||||
|
help="extra argument passed to gcc; repeat for multiple args",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--std-c",
|
||||||
|
type=Path,
|
||||||
|
default=Path("tests/std.c"),
|
||||||
|
help="C runtime source compiled with the assembly file (default: tests/std.c)",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--no-std-c",
|
||||||
|
action="store_true",
|
||||||
|
help="do not compile tests/std.c with the assembly file",
|
||||||
|
)
|
||||||
|
args = parser.parse_args(tool_args)
|
||||||
|
args.program_args = program_args
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
def require_command(command: str) -> None:
|
||||||
|
if shutil.which(command) is None:
|
||||||
|
print(f"error: command not found: {command}", file=sys.stderr)
|
||||||
|
sys.exit(127)
|
||||||
|
|
||||||
|
|
||||||
|
def run_command(command: list[str]) -> subprocess.CompletedProcess[bytes]:
|
||||||
|
try:
|
||||||
|
return subprocess.run(command, capture_output=True, check=False)
|
||||||
|
except OSError as err:
|
||||||
|
print(f"error: failed to run {command[0]}: {err}", file=sys.stderr)
|
||||||
|
sys.exit(127)
|
||||||
|
|
||||||
|
|
||||||
|
def choose_gdb_command(requested_command: str | None) -> str:
|
||||||
|
if requested_command:
|
||||||
|
return requested_command
|
||||||
|
|
||||||
|
for command in ("arm-linux-gnueabihf-gdb", "gdb-multiarch", "gdb"):
|
||||||
|
if shutil.which(command) is not None:
|
||||||
|
return command
|
||||||
|
|
||||||
|
return "gdb-multiarch"
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
args = parse_args()
|
||||||
|
asm_file = args.asm_file
|
||||||
|
if not asm_file.is_file():
|
||||||
|
print(f"error: assembly file not found: {asm_file}", file=sys.stderr)
|
||||||
|
return 2
|
||||||
|
|
||||||
|
std_c = args.std_c
|
||||||
|
if not args.no_std_c and not std_c.is_file():
|
||||||
|
print(f"error: std C file not found: {std_c}", file=sys.stderr)
|
||||||
|
return 2
|
||||||
|
|
||||||
|
require_command(args.cc)
|
||||||
|
require_command(args.qemu)
|
||||||
|
|
||||||
|
with tempfile.TemporaryDirectory(prefix="run-arm-asm-") as temp_dir:
|
||||||
|
output = args.output or Path(temp_dir) / asm_file.with_suffix("").name
|
||||||
|
|
||||||
|
compile_cmd = [args.cc]
|
||||||
|
if not args.no_static:
|
||||||
|
compile_cmd.append("-static")
|
||||||
|
if args.gdb:
|
||||||
|
compile_cmd.append("-g")
|
||||||
|
compile_cmd.extend(args.gcc_arg)
|
||||||
|
compile_cmd.append(str(asm_file))
|
||||||
|
if not args.no_std_c:
|
||||||
|
compile_cmd.append(str(std_c))
|
||||||
|
compile_cmd.extend(["-o", str(output)])
|
||||||
|
|
||||||
|
compile_result = run_command(compile_cmd)
|
||||||
|
sys.stdout.buffer.write(compile_result.stdout)
|
||||||
|
sys.stderr.buffer.write(compile_result.stderr)
|
||||||
|
if compile_result.returncode != 0:
|
||||||
|
return compile_result.returncode
|
||||||
|
|
||||||
|
qemu_cmd = [args.qemu]
|
||||||
|
if args.gdb:
|
||||||
|
qemu_cmd.extend(["-g", args.gdb_port])
|
||||||
|
resolved_output = output.resolve()
|
||||||
|
gdb_command = choose_gdb_command(args.gdb_command)
|
||||||
|
print(f"executable: {resolved_output}", file=sys.stderr, flush=True)
|
||||||
|
print(
|
||||||
|
f"gdb: {gdb_command} {resolved_output} "
|
||||||
|
f"-ex 'target remote :{args.gdb_port}'",
|
||||||
|
file=sys.stderr,
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
print("gdb: use 'si' or 'c'; do not use 'run'", file=sys.stderr, flush=True)
|
||||||
|
qemu_cmd.extend([str(output), *args.program_args])
|
||||||
|
|
||||||
|
run_result = run_command(qemu_cmd)
|
||||||
|
sys.stdout.buffer.write(run_result.stdout)
|
||||||
|
sys.stderr.buffer.write(run_result.stderr)
|
||||||
|
if run_result.stdout and not run_result.stdout.endswith(b"\n"):
|
||||||
|
print()
|
||||||
|
print(f"$? = {run_result.returncode}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
Reference in New Issue
Block a user