Files
TransPyC/lib/Projectrans/Config.py

215 lines
8.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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()