feat(tools): Add test tool and std.c
This commit is contained in:
+3
-1
@@ -1,3 +1,5 @@
|
||||
/target
|
||||
/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