可用的回归测试通过的标准版本

This commit is contained in:
2026-06-18 00:39:43 +08:00
parent bffb0cb6b7
commit e02c867edf
365 changed files with 22562 additions and 24532 deletions

214
lib/Projectrans/Config.py Normal file
View File

@@ -0,0 +1,214 @@
import os
import sys
import shutil
from lib.Projectrans.Phase1Generator import Phase1Generator
from lib.Projectrans.Phase2Translator import Phase2Translator
def save_sha1_map(temp_dir: str, sha1_map: dict[str, str]):
"""将 SHA1 映射表保存到文件"""
map_path = os.path.join(temp_dir, '_sha1_map.txt')
with open(map_path, 'w', encoding='utf-8') as f:
for sha1, rel in sorted(sha1_map.items()):
f.write(f"{sha1}:{rel}\n")
def Load_sha1_map(temp_dir: str) -> dict[str, str]:
"""从文件加载 SHA1 映射表"""
map_path = os.path.join(temp_dir, '_sha1_map.txt')
result = {}
if os.path.exists(map_path):
with open(map_path, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if ':' in line:
sha1, rel = line.split(':', 1)
result[sha1] = rel
return result
def Load_project_config(project_file: str) -> dict:
"""从 project.json 加载配置"""
import json
with open(project_file, 'r', encoding='utf-8') as f:
return json.load(f)
def resolve_paths(config: dict, project_file: str) -> dict:
"""将配置中的相对路径转换为绝对路径"""
project_dir = os.path.dirname(os.path.abspath(project_file))
result = dict(config)
def resolve(p):
if p is None:
return None
if os.path.isabs(p):
return p
return os.path.normpath(os.path.join(project_dir, p))
if 'source_dir' in result:
result['source_dir'] = resolve(result.get('source_dir'))
elif 'sources' in result:
resolved_sources = []
for src in result['sources']:
if '*' in src or '?' in src:
import glob as glob_module
for matched in glob_module.glob(os.path.join(project_dir, src), recursive=True):
if os.path.isfile(matched):
resolved_sources.append(matched)
else:
resolved_sources.append(resolve(src))
result['sources'] = [s for s in resolved_sources if os.path.isfile(s)]
result['temp_dir'] = resolve(config.get('temp_dir'))
result['output_dir'] = resolve(config.get('output_dir'))
if 'includes' in result:
result['includes'] = [resolve(inc) for inc in result['includes']]
result['_project_dir'] = project_dir
return result
def main():
import argparse
parser = argparse.ArgumentParser(
description='TransPyC 工程翻译器 - 基于 project.json 的两阶段翻译',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog='''
示例:
# 从 project.json 读取配置并执行(默认查找当前目录)
python ProjectTrans.py
# 指定 project.json 文件
python ProjectTrans.py --project ./TestProject/project.json
# 使用命令行参数覆盖 project.json 配置
python ProjectTrans.py --src ./src --temp ./decl --output ./out --phase all
# 仅运行阶段一
python ProjectTrans.py --phase 1
# 仅运行阶段二
python ProjectTrans.py --phase 2
# 清理临时目录
python ProjectTrans.py --clean
'''
)
parser.add_argument('--project', help='project.json 路径(默认查找当前目录)')
parser.add_argument('--src', help='源文件目录(覆盖 project.json')
parser.add_argument('--temp', help='声明接口临时目录(覆盖 project.json')
parser.add_argument('--output', help='输出目录(覆盖 project.json')
parser.add_argument('--phase', choices=['1', '2', 'all'], help='阶段: 1=生成声明, 2=翻译+编译, all=全部')
parser.add_argument('--cc', help='LLVM 编译器命令(覆盖 project.json')
parser.add_argument('--clean', action='store_true', help='清理 output 和 temp 目录')
args = parser.parse_args()
project_file = args.project
if not project_file:
candidates = ['project.json', './project.json']
for c in candidates:
if os.path.exists(c):
project_file = c
break
if project_file and os.path.exists(project_file):
print(f"[配置] 读取: {project_file}")
config = Load_project_config(project_file)
project_dir = os.path.dirname(os.path.abspath(project_file))
print(f"[配置] 项目目录: {project_dir}")
else:
print("[错误] 未找到 project.json请使用 --project 指定")
sys.exit(1)
resolved = resolve_paths(config, project_file)
src = args.src or resolved.get('source_dir') or resolved.get('sources')
temp_dir = args.temp or resolved.get('temp_dir')
output_dir = args.output or resolved.get('output_dir')
phase = args.phase or 'all'
cc_cmd = args.cc or resolved.get('compiler', {}).get('cmd', 'llc')
cc_flags = resolved.get('compiler', {}).get('flags', ['-filetype=obj'])
slice_level = resolved.get('options', {}).get('slice_level', 3)
project_include_dirs = resolved.get('includes', [])
target_triple = resolved.get('target', {}).get('triple')
target_datalayout = resolved.get('target', {}).get('datalayout')
if isinstance(src, list):
if len(src) == 1 and os.path.isdir(src[0]):
src = src[0]
else:
src = src[0] if len(src) == 1 else os.path.dirname(os.path.commonpath(src)) if all(os.path.exists(p) for p in src) else src[0]
print(f"[配置] 源目录: {src}")
print(f"[配置] 临时目录: {temp_dir}")
print(f"[配置] 输出目录: {output_dir}")
print(f"[配置] 阶段: {phase}")
print(f"[配置] 编译器: {cc_cmd} {' '.join(cc_flags)}")
if target_triple:
print(f"[配置] 目标三元组: {target_triple}")
if target_datalayout:
print(f"[配置] 数据布局: {target_datalayout}")
if args.clean:
if output_dir and os.path.exists(output_dir):
for f in os.listdir(output_dir):
fpath = os.path.join(output_dir, f)
try:
if os.path.isfile(fpath) or os.path.islink(fpath):
os.remove(fpath)
elif os.path.isdir(fpath):
shutil.rmtree(fpath, ignore_errors=True)
except:
pass
print(f"[清理] output: 已清空 {output_dir}")
if temp_dir and os.path.exists(temp_dir):
for f in os.listdir(temp_dir):
fpath = os.path.join(temp_dir, f)
try:
if os.path.isfile(fpath) or os.path.islink(fpath):
os.remove(fpath)
elif os.path.isdir(fpath):
shutil.rmtree(fpath, ignore_errors=True)
except:
pass
print(f"[清理] temp: 已清空 {temp_dir}")
print("[清理] 完成")
if (not phase or phase == 'all') and not args.project:
return
if phase in ('1', 'all'):
if isinstance(src, list):
print("[错误] 阶段一需要指定源目录,不支持文件列表")
sys.exit(1)
all_include_dirs = list(project_include_dirs) if project_include_dirs else []
for d in Phase2Translator.INCLUDE_DIRS:
if os.path.isdir(d):
d = os.path.abspath(d)
if d not in [os.path.abspath(x) for x in all_include_dirs]:
all_include_dirs.append(d)
gen = Phase1Generator(src, temp_dir, include_dirs=all_include_dirs, target_triple=target_triple, target_datalayout=target_datalayout)
gen.run()
save_sha1_map(temp_dir, gen.sha1_map)
if phase in ('2', 'all'):
if not output_dir:
print("[错误] 阶段二需要指定 --output 目录")
sys.exit(1)
if not os.path.exists(temp_dir):
print(f"[错误] 声明目录不存在,请先运行阶段一: {temp_dir}")
sys.exit(1)
if isinstance(src, list):
print("[错误] 阶段二需要指定源目录,不支持文件列表")
sys.exit(1)
linker_cmd = resolved.get('linker', {}).get('cmd')
linker_flags = resolved.get('linker', {}).get('flags', [])
linker_output = resolved.get('linker', {}).get('output')
target_triple = resolved.get('target', {}).get('triple')
target_datalayout = resolved.get('target', {}).get('datalayout')
startup = resolved.get('options', {}).get('startup')
trans = Phase2Translator(src, temp_dir, output_dir, compile_cmd=cc_cmd, compile_flags=cc_flags, linker_cmd=linker_cmd, linker_flags=linker_flags, linker_output=linker_output, include_dirs=project_include_dirs, target_triple=target_triple, target_datalayout=target_datalayout, startup=startup)
trans.sha1_map = Load_sha1_map(temp_dir)
trans.slice_level = slice_level
trans.run()