feat(tools): Add test tool and std.c

This commit is contained in:
2026-05-13 08:48:07 +08:00
parent af72ed08c3
commit 6b475fb577
3 changed files with 301 additions and 1 deletions
+3 -1
View File
@@ -1,3 +1,5 @@
/target /target
/testcases /testcases
/output /output
*.pyc
*.zip
+121
View File
@@ -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);
}
+177
View File
@@ -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())