原改写用字面 replace("--oformat binary"/"kernel.bin"),若 .bat 里的写法是
"--oformat=binary" 或输出名大小写/路径不同就会匹配不中——那种情况下执行的
仍是原始命令,会把 kernel.bin 覆盖成非预期产物。现改为正则同时兜住
"--oformat binary"/"--oformat=binary"、"-o <path>"/"--output=<path>",
并加保护:改写后若不包含 kernel.elf 则直接跳过,绝不执行未改写的命令。
193 lines
7.7 KiB
Python
193 lines
7.7 KiB
Python
import shutil
|
||
import os
|
||
import re
|
||
import sys
|
||
import subprocess
|
||
import threading
|
||
|
||
|
||
# 解析参数
|
||
clean = '--noclean' not in sys.argv
|
||
kernel_only = '--kernel-only' in sys.argv
|
||
noapp = '--noapp' in sys.argv
|
||
bootloader = '--bootloader' in sys.argv
|
||
|
||
|
||
# 编译内核
|
||
cmd = r'..\..\ViperC\output\viperc.exe --project ./VKernel/project.vpj --clear-cache --rebuild-includes'
|
||
if clean:
|
||
cmd += ' --clean'
|
||
os.system(cmd)
|
||
|
||
|
||
shutil.copy("VKernel/__vpcbuild__/output/kernel.bin", "./disk/sys/kernel/x86_64/kernel.bin")
|
||
|
||
|
||
# 额外产出带符号的 kernel.elf(仅供分析:objdump / nm / readelf 定位崩溃 rip)。
|
||
# kernel.bin 是 --oformat binary --strip-all 的裸镜像、无符号表,不便把日志里的
|
||
# rip 映射回函数。要点:**对象顺序决定段内布局**,自行 glob 一组 .obj 去链会得到
|
||
# 与 kernel.bin 不同的地址(实测前 247KB 里 230KB 不同),没法对上日志。
|
||
# 因此用 viperc 自己的 --phase2command:它把 llc/链接命令按“精确顺序”写进 .bat,
|
||
# 我们只把该链接行里的 --oformat binary/--strip-all 去掉、输出改为 kernel.elf 再执行。
|
||
kernel_elf = "VKernel/__vpcbuild__/output/kernel.elf"
|
||
p2c_bat = "VKernel/__vpcbuild__/phase2cmd.bat"
|
||
os.system(r'..\..\ViperC\output\viperc.exe --project ./VKernel/project.vpj --phase2command ' + p2c_bat)
|
||
link_line = None
|
||
if os.path.exists(p2c_bat):
|
||
with open(p2c_bat, "r", encoding="utf-8", errors="replace") as f:
|
||
for line in f:
|
||
low = line.lower()
|
||
if "ld.lld" in low and (".bin" in low or "output" in low):
|
||
link_line = line.rstrip("\r\n")
|
||
if link_line is not None:
|
||
# 稳健改写:--oformat 可能是 "--oformat binary" 或 "--oformat=binary";输出可能是
|
||
# "-o <path>" 或 "--output=<path>"。任一没匹配上就放弃(绝不去执行原样命令,否则会把
|
||
# kernel.bin 覆盖成非预期产物)。
|
||
elf_line = re.sub(r"--oformat[ =]binary", "", link_line)
|
||
elf_line = re.sub(r"--strip-all", "", elf_line)
|
||
elf_line = re.sub(r"(-o\s+)\S+", r"\1" + kernel_elf, elf_line)
|
||
elf_line = re.sub(r"(--output=)\S+", r"\1" + kernel_elf, elf_line)
|
||
if elf_line == link_line or kernel_elf not in elf_line:
|
||
print("[build] 警告: 未能改写链接命令,跳过 kernel.elf(避免误覆盖 kernel.bin)")
|
||
else:
|
||
print("[build] linking kernel.elf for analysis ...")
|
||
if os.system(elf_line) != 0:
|
||
print("[build] 警告: kernel.elf 链接失败(不影响 kernel.bin)")
|
||
else:
|
||
print("[build] 警告: 未从 phase2command 取到链接命令,跳过 kernel.elf")
|
||
|
||
|
||
# --noapp 优先级高于 --kernel-only:只编译内核,不编译所有库和应用
|
||
if noapp:
|
||
print("[build] --noapp: 跳过所有库与应用编译,复用已有缓存文件")
|
||
# 检查必要文件是否存在
|
||
required_libs = [
|
||
("Libs/SerialLogger/__vpcbuild__/output/slog.so", "./disk/libs/slog.so"),
|
||
("Libs/VPUI64/__vpcbuild__/output/vpui64.so", "./disk/libs/vpui64.so"),
|
||
]
|
||
required_apps = [
|
||
("Apps/HelloWorld/__vpcbuild__/output/helloworld.elf", "./disk/apps/hello.elf"),
|
||
("Apps/Terminal/__vpcbuild__/output/terminal.elf", "./disk/apps/terminal.elf"),
|
||
("Apps/Gargantua/__vpcbuild__/output/gargantua.elf", "./disk/apps/gargantua.elf"),
|
||
("Apps/Scene3D/__vpcbuild__/output/scene3d.elf", "./disk/apps/scene3d.elf"),
|
||
]
|
||
os.makedirs("./disk/apps", exist_ok=True)
|
||
os.makedirs("./disk/libs", exist_ok=True)
|
||
for src, dst in required_libs + required_apps:
|
||
if os.path.exists(src):
|
||
shutil.copy(src, dst)
|
||
else:
|
||
print(f"[build] 警告: {src} 不存在,请先完整构建一次")
|
||
elif kernel_only:
|
||
# 仅 --kernel-only:只跳过应用,不跳过库(原有逻辑保留)
|
||
print("[build] --kernel-only: 跳过其他应用和库的编译")
|
||
if not os.path.exists("./disk/apps/hello.elf"):
|
||
print("[build] 警告: hello.elf 不存在,请先不带 --kernel-only 完整构建一次")
|
||
else:
|
||
# 完整编译所有库+应用
|
||
so_cmd = r'..\..\ViperC\output\viperc.exe --project Libs/SerialLogger/project.vpj --clear-cache'
|
||
if clean:
|
||
so_cmd += ' --clean'
|
||
os.system(so_cmd)
|
||
|
||
vpui_cmd = r'..\..\ViperC\output\viperc.exe --project Libs/VPUI64/project.vpj --clear-cache'
|
||
if clean:
|
||
vpui_cmd += ' --clean'
|
||
os.system(vpui_cmd)
|
||
|
||
app_cmd = r'..\..\ViperC\output\viperc.exe --project Apps/HelloWorld/project.vpj --clear-cache'
|
||
if clean:
|
||
app_cmd += ' --clean'
|
||
os.system(app_cmd)
|
||
|
||
os.makedirs("./disk/apps", exist_ok=True)
|
||
shutil.copy("Apps/HelloWorld/__vpcbuild__/output/helloworld.elf", "./disk/apps/hello.elf")
|
||
|
||
term_cmd = r'..\..\ViperC\output\viperc.exe --project Apps/Terminal/project.vpj --clear-cache'
|
||
if clean:
|
||
term_cmd += ' --clean'
|
||
os.system(term_cmd)
|
||
|
||
shutil.copy("Apps/Terminal/__vpcbuild__/output/terminal.elf", "./disk/apps/terminal.elf")
|
||
|
||
garg_cmd = r'..\..\ViperC\output\viperc.exe --project Apps/Gargantua/project.vpj --clear-cache'
|
||
if clean:
|
||
garg_cmd += ' --clean'
|
||
os.system(garg_cmd)
|
||
|
||
shutil.copy("Apps/Gargantua/__vpcbuild__/output/gargantua.elf", "./disk/apps/gargantua.elf")
|
||
|
||
scene3d_cmd = r'..\..\ViperC\output\viperc.exe --project Apps/Scene3D/project.vpj --clear-cache'
|
||
if clean:
|
||
scene3d_cmd += ' --clean'
|
||
os.system(scene3d_cmd)
|
||
|
||
shutil.copy("Apps/Scene3D/__vpcbuild__/output/scene3d.elf", "./disk/apps/scene3d.elf")
|
||
|
||
os.makedirs("./disk/libs", exist_ok=True)
|
||
so_path = os.path.abspath("Libs/SerialLogger/__vpcbuild__/output/slog.so")
|
||
vpui_path = os.path.abspath("Libs/VPUI64/__vpcbuild__/output/vpui64.so")
|
||
if os.path.exists(so_path):
|
||
shutil.copy(so_path, "./disk/libs/slog.so")
|
||
if os.path.exists(vpui_path):
|
||
shutil.copy(vpui_path, "./disk/libs/vpui64.so")
|
||
|
||
|
||
# 编译 bootloader(GNU-EFI:MSYS make → WSL Ubuntu);
|
||
# bootx64.efi 不由 viperc 产出,容易忘记重编而一直用旧引导器(改了 boot.c / 内核预留逻辑
|
||
# 却仍在用旧件)。显式传 --bootloader 才重编;未传时若 boot.c 比产物新则给出提醒。
|
||
boot_src = "boot/boot.c"
|
||
boot_out = "disk/EFI/BOOT/bootx64.efi"
|
||
if bootloader:
|
||
print("[build] building bootloader (make -C boot)...")
|
||
boot_rc = os.system('make -C boot')
|
||
if boot_rc != 0:
|
||
print("[build] 警告: bootloader 构建失败(需 MSYS make + WSL Ubuntu + _UNCADK/tools/gnu-efi)")
|
||
elif os.path.exists(boot_src) and os.path.exists(boot_out) and os.path.getmtime(boot_src) > os.path.getmtime(boot_out):
|
||
print("[build] 警告: boot/boot.c 比 disk/EFI/BOOT/bootx64.efi 新 → bootloader 已过期,请用 --bootloader 重编")
|
||
|
||
|
||
os.system('powershell -ExecutionPolicy Bypass -File ./Scripts/disk.ps1')
|
||
|
||
|
||
serial_log = os.path.abspath("serial.log")
|
||
if os.path.exists(serial_log):
|
||
os.remove(serial_log)
|
||
|
||
|
||
print(f"[build] CWD: {os.getcwd()}")
|
||
print(f"[build] serial_log: {serial_log}")
|
||
|
||
|
||
qemu_cmd = [
|
||
'qemu-system-x86_64',
|
||
'-pflash', r'D:\msys64\mingw64\share\qemu\edk2-x86_64-code.fd',
|
||
'-drive', 'file=disk.img,format=raw,if=ide',
|
||
'-m', '256m',
|
||
'-vga', 'std',
|
||
'-usb',
|
||
'-device', 'usb-kbd',
|
||
'-device', 'usb-mouse',
|
||
'-accel', 'tcg,tb-size=128',
|
||
'-serial', f'file:{serial_log}',
|
||
'-no-reboot'
|
||
]
|
||
|
||
|
||
print(f"[build] QEMU cmd: {' '.join(qemu_cmd)}")
|
||
|
||
|
||
proc = subprocess.Popen(qemu_cmd)
|
||
proc.wait()
|
||
|
||
|
||
print(f"[build] serial_log path: {serial_log}")
|
||
print(f"[build] file exists: {os.path.exists(serial_log)}")
|
||
if os.path.exists(serial_log):
|
||
with open(serial_log, "r", encoding="utf-8", errors="replace") as f:
|
||
content = f.read()
|
||
print(f"[build] serial_log size: {len(content)} bytes")
|
||
print(content)
|
||
else:
|
||
print("[build] serial.log not found!")
|