修正了种子编译器的错误
This commit is contained in:
@@ -69,8 +69,17 @@ def resolve_paths(config: dict[str, Any], project_file: str) -> dict[str, Any]:
|
||||
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'))
|
||||
# build_dir: 统一构建目录(默认 ./.tpv_build),temp 和 output 作为其子目录
|
||||
# 不再单独读取 temp_dir/output_dir,由 build_dir 自动计算
|
||||
build_dir: str = config.get('build_dir', './.tpv_build')
|
||||
build_dir = resolve(build_dir)
|
||||
result['build_dir'] = build_dir
|
||||
if build_dir is not None:
|
||||
result['temp_dir'] = os.path.join(build_dir, 'temp')
|
||||
result['output_dir'] = os.path.join(build_dir, 'output')
|
||||
else:
|
||||
result['temp_dir'] = None
|
||||
result['output_dir'] = None
|
||||
|
||||
if 'includes' in result:
|
||||
result['includes'] = [resolve(inc) for inc in result['includes']]
|
||||
|
||||
@@ -110,8 +110,9 @@ class DeclarationGenerator:
|
||||
if decl:
|
||||
lines.append(decl)
|
||||
elif isinstance(node, ast.ClassDef):
|
||||
if hasattr(node, 'type_params') and node.type_params:
|
||||
continue
|
||||
# PEP 695 泛型类不再完全跳过:子类(如 Value(GSListNode[Value]))的
|
||||
# 继承字段展平需要从泛型基类的 stub 中读取字段(如 GSListNode.Next)。
|
||||
# _generate_class_decl 内部会跳过泛型类的方法声明(T 参数 → opaque struct)。
|
||||
decls: list[str] = self._generate_class_decl(node)
|
||||
lines.extend(decls)
|
||||
|
||||
@@ -625,37 +626,42 @@ class DeclarationGenerator:
|
||||
else:
|
||||
new_func_decl = f'declare void @{new_func_name}({struct_type_name}*)'
|
||||
decls.append(new_func_decl)
|
||||
for item in node.body:
|
||||
if isinstance(item, ast.FunctionDef):
|
||||
if hasattr(item, 'type_params') and item.type_params:
|
||||
continue
|
||||
method_name: str = f'{class_name}.{item.name}'
|
||||
if self.module_sha1:
|
||||
method_name = f"{self.module_sha1}.{method_name}"
|
||||
ret_type: str = self._get_type_str(item.returns) if item.returns else 'void'
|
||||
if not ret_type:
|
||||
ret_type = 'void'
|
||||
params: list[str] = []
|
||||
for arg_idx, arg in enumerate(item.args.args):
|
||||
arg_type: str
|
||||
if arg.annotation:
|
||||
arg_type = self._get_type_str(arg.annotation)
|
||||
elif arg_idx == 0 and arg.arg == 'self':
|
||||
arg_type = f'{struct_type_name}*'
|
||||
# PEP 695 泛型类跳过方法声明生成:方法参数类型 T 会被解析为 opaque struct,
|
||||
# LLVM 报 "invalid type for function argument"。字段(AnnAssign)仍正常生成,
|
||||
# 确保子类(如 Value(GSListNode[Value]))能从 stub 中读取继承字段(如 Next)。
|
||||
IsGenericClass: bool = hasattr(node, 'type_params') and bool(node.type_params)
|
||||
if not IsGenericClass:
|
||||
for item in node.body:
|
||||
if isinstance(item, ast.FunctionDef):
|
||||
if hasattr(item, 'type_params') and item.type_params:
|
||||
continue
|
||||
method_name: str = f'{class_name}.{item.name}'
|
||||
if self.module_sha1:
|
||||
method_name = f"{self.module_sha1}.{method_name}"
|
||||
ret_type: str = self._get_type_str(item.returns) if item.returns else 'void'
|
||||
if not ret_type:
|
||||
ret_type = 'void'
|
||||
params: list[str] = []
|
||||
for arg_idx, arg in enumerate(item.args.args):
|
||||
arg_type: str
|
||||
if arg.annotation:
|
||||
arg_type = self._get_type_str(arg.annotation)
|
||||
elif arg_idx == 0 and arg.arg == 'self':
|
||||
arg_type = f'{struct_type_name}*'
|
||||
else:
|
||||
arg_type = 'i8*'
|
||||
# C 语言中数组参数退化为指针:[N x elem_type] → elem_type*
|
||||
arg_type = self._decay_array_to_ptr(arg_type)
|
||||
params.append(arg_type)
|
||||
param_str: str = ', '.join(params) if params else ''
|
||||
if method_name[0].isdigit():
|
||||
decls.append(f'declare {ret_type} @"{method_name}"({param_str})')
|
||||
else:
|
||||
arg_type = 'i8*'
|
||||
# C 语言中数组参数退化为指针:[N x elem_type] → elem_type*
|
||||
arg_type = self._decay_array_to_ptr(arg_type)
|
||||
params.append(arg_type)
|
||||
param_str: str = ', '.join(params) if params else ''
|
||||
if method_name[0].isdigit():
|
||||
decls.append(f'declare {ret_type} @"{method_name}"({param_str})')
|
||||
else:
|
||||
decls.append(f'declare {ret_type} @{method_name}({param_str})')
|
||||
# 为继承但未覆写的方法生成包装声明
|
||||
# 这些声明让跨模块调用能正确解析子类包装函数的签名,
|
||||
# 避免 stub 缺失导致默认 i32 返回类型 → 64 位指针截断
|
||||
decls.extend(self._generate_inherited_method_decls(node, class_name, struct_type_name))
|
||||
decls.append(f'declare {ret_type} @{method_name}({param_str})')
|
||||
# 为继承但未覆写的方法生成包装声明
|
||||
# 这些声明让跨模块调用能正确解析子类包装函数的签名,
|
||||
# 避免 stub 缺失导致默认 i32 返回类型 → 64 位指针截断
|
||||
decls.extend(self._generate_inherited_method_decls(node, class_name, struct_type_name))
|
||||
return decls
|
||||
|
||||
def _generate_inherited_method_decls(self, node: ast.ClassDef, child_class_name: str, child_struct_type: str) -> list[str]:
|
||||
|
||||
@@ -59,6 +59,10 @@ class Phase1Generator:
|
||||
self._manifest: dict[str, dict] = {}
|
||||
self._typedef_map_cache: dict[str, ast.AST] | None = None
|
||||
self._manifest_path: str = os.path.join(self.temp_dir, '_phase1_manifest.json')
|
||||
# includes 依赖图:{module_name: set[dep_module_name]},用于 .stub.ll 缓存依赖校验
|
||||
self._include_dep_graph: dict[str, set[str]] = {}
|
||||
# include 文件映射:{module_name: (src_path, rel_from_inc, includes_dir)}
|
||||
self._include_file_map: dict[str, tuple[str, str, str]] = {}
|
||||
self._load_manifest()
|
||||
|
||||
def _load_manifest(self) -> None:
|
||||
@@ -174,6 +178,7 @@ class Phase1Generator:
|
||||
pass
|
||||
|
||||
needed: set[str] = set()
|
||||
dep_graph: dict[str, set[str]] = {}
|
||||
queue: list[str] = list(imported_from_src)
|
||||
while queue:
|
||||
mod_name: str = queue.pop(0)
|
||||
@@ -184,6 +189,7 @@ class Phase1Generator:
|
||||
src_path: str = include_file_map[mod_name][0]
|
||||
includes_dir: str = include_file_map[mod_name][2]
|
||||
deps: set[str] = get_file_dependencies(src_path, includes_dir)
|
||||
dep_graph[mod_name] = deps
|
||||
for dep in deps:
|
||||
if dep in include_file_map and dep not in needed:
|
||||
queue.append(dep)
|
||||
@@ -195,6 +201,10 @@ class Phase1Generator:
|
||||
if info not in needed_infos:
|
||||
needed_infos.append(info)
|
||||
|
||||
# 存储依赖图和文件映射,供 .stub.ll 缓存依赖校验使用
|
||||
self._include_dep_graph = dep_graph
|
||||
self._include_file_map = include_file_map
|
||||
|
||||
return needed_infos
|
||||
|
||||
def run(self) -> None:
|
||||
@@ -356,6 +366,58 @@ class Phase1Generator:
|
||||
_vlog().warning(f"收集 typedef 映射失败: {_e}", "Exception")
|
||||
return typedef_map
|
||||
|
||||
def _ComputeDepSha1Signature(self, module_name: str) -> str:
|
||||
"""计算 module_name 的所有传递依赖的 SHA1 签名。
|
||||
|
||||
用于 .stub.ll 缓存依赖校验:当依赖的 include SHA1 变化时,
|
||||
当前 include 的 .stub.ll 缓存应失效(因为类型引用的 SHA1 前缀会变化)。
|
||||
"""
|
||||
if not self._include_dep_graph or not self._include_file_map:
|
||||
return ''
|
||||
Result: set[str] = set()
|
||||
Visited: set[str] = set()
|
||||
Queue: list[str] = list(self._include_dep_graph.get(module_name, set()))
|
||||
while Queue:
|
||||
DepMod: str = Queue.pop(0)
|
||||
if DepMod in Visited:
|
||||
continue
|
||||
Visited.add(DepMod)
|
||||
if DepMod in self._include_file_map:
|
||||
DepSrc: str = self._include_file_map[DepMod][0]
|
||||
DepSha1: str = self._get_sha1(DepSrc)
|
||||
if DepSha1:
|
||||
Result.add(DepSha1)
|
||||
Queue.extend(self._include_dep_graph.get(DepMod, set()))
|
||||
return '|'.join(sorted(Result))
|
||||
|
||||
def _ReadStubDep(self, stub_path: str) -> str:
|
||||
"""读取 .stub.ll 的依赖签名 sidecar,不存在返回空字符串。"""
|
||||
DepPath: str = stub_path + '.dep'
|
||||
try:
|
||||
with open(DepPath, 'r', encoding='utf-8') as f:
|
||||
return f.read()
|
||||
except Exception:
|
||||
return ''
|
||||
|
||||
def _WriteStubDep(self, stub_path: str, dep_sig: str) -> None:
|
||||
"""写入 .stub.ll 的依赖签名 sidecar 到 temp_dir 和全局缓存。"""
|
||||
# temp_dir
|
||||
DepPath: str = stub_path + '.dep'
|
||||
try:
|
||||
with open(DepPath, 'w', encoding='utf-8') as f:
|
||||
f.write(dep_sig)
|
||||
except Exception:
|
||||
pass
|
||||
# 全局缓存
|
||||
Sha1: str = os.path.basename(stub_path).replace('.stub.ll', '')
|
||||
GlobalDepPath: str = os.path.join(_GLOBAL_CACHE_DIR, f"{Sha1}.stub.ll.dep")
|
||||
try:
|
||||
os.makedirs(_GLOBAL_CACHE_DIR, exist_ok=True)
|
||||
with open(GlobalDepPath, 'w', encoding='utf-8') as f:
|
||||
f.write(dep_sig)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _process_include_py_files_stub(self, struct_names: set[str], needed_includes: list[tuple[str, str, str]] | None = None, enum_names: set[str] | None = None, struct_sha1_map: dict[str, str] | None = None, exception_names: set[str] | None = None, class_def_map: dict[str, ast.ClassDef] | None = None) -> None:
|
||||
include_py_files: list[tuple[str, str, str]]
|
||||
if needed_includes is not None:
|
||||
@@ -374,15 +436,43 @@ class Phase1Generator:
|
||||
if not sha1:
|
||||
continue
|
||||
|
||||
# 计算依赖签名(用于 .stub.ll 缓存依赖校验)
|
||||
# 当依赖的 include SHA1 变化时,.stub.ll 中类型引用的 SHA1 前缀会变化,缓存应失效
|
||||
DepSig: str = self._ComputeDepSha1Signature(module_name)
|
||||
|
||||
stub_path: str = os.path.join(self.temp_dir, f"{sha1}.stub.ll")
|
||||
if os.path.isfile(stub_path):
|
||||
_vlog().info(f" 缓存命中: {rel_from_inc} -> {sha1}.stub.ll")
|
||||
continue
|
||||
# 依赖校验:temp_dir 中的 .stub.ll 可能是上次编译残留,引用旧 SHA1
|
||||
if self._ReadStubDep(stub_path) == DepSig:
|
||||
_vlog().info(f" 缓存命中: {rel_from_inc} -> {sha1}.stub.ll")
|
||||
continue
|
||||
else:
|
||||
_vlog().info(f" 缓存失效(依赖变更): {rel_from_inc} -> {sha1}.stub.ll")
|
||||
try:
|
||||
os.remove(stub_path)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 全局缓存检查(不被 --clean 清除)
|
||||
if _TryGlobalCache(sha1, 'stub.ll', stub_path):
|
||||
_vlog().info(f" 缓存命中(全局): {rel_from_inc} -> {sha1}.stub.ll")
|
||||
continue
|
||||
# 从全局缓存复制 sidecar .dep 文件
|
||||
GlobalDepPath: str = os.path.join(_GLOBAL_CACHE_DIR, f"{sha1}.stub.ll.dep")
|
||||
TempDepPath: str = stub_path + '.dep'
|
||||
if os.path.isfile(GlobalDepPath):
|
||||
try:
|
||||
shutil.copy2(GlobalDepPath, TempDepPath)
|
||||
except Exception:
|
||||
pass
|
||||
# 依赖校验:全局缓存中的 .stub.ll 可能引用旧 SHA1
|
||||
if self._ReadStubDep(stub_path) == DepSig:
|
||||
_vlog().info(f" 缓存命中(全局): {rel_from_inc} -> {sha1}.stub.ll")
|
||||
continue
|
||||
else:
|
||||
_vlog().info(f" 缓存失效(依赖变更): {rel_from_inc} -> {sha1}.stub.ll")
|
||||
try:
|
||||
os.remove(stub_path)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
sig_path: str = os.path.join(self.temp_dir, f"{sha1}.pyi")
|
||||
if not os.path.isfile(sig_path):
|
||||
@@ -392,6 +482,8 @@ class Phase1Generator:
|
||||
|
||||
self._generate_decl_ll(sig_content, stub_path, src_path, struct_names, enum_names=enum_names, module_sha1=sha1, struct_sha1_map=struct_sha1_map, exception_names=exception_names, typedef_map=typedef_map, class_def_map=class_def_map)
|
||||
_SaveToGlobalCache(sha1, 'stub.ll', stub_path)
|
||||
# 写入依赖签名 sidecar(temp_dir 和全局缓存)
|
||||
self._WriteStubDep(stub_path, DepSig)
|
||||
_vlog().info(f" 生成声明: {rel_from_inc} -> {sha1}.stub.ll")
|
||||
except Exception as e:
|
||||
_vlog().error(f"处理 include 文件失败 {rel_from_inc}: {e}")
|
||||
|
||||
@@ -458,7 +458,10 @@ class Phase2Translator:
|
||||
raise
|
||||
|
||||
if errors:
|
||||
_vlog().error(f"\n{len(errors)} 个文件翻译失败,继续编译 includes")
|
||||
_vlog().error(f"\n{len(errors)} 个文件翻译失败,立即终止编译")
|
||||
for rel, err in errors:
|
||||
_vlog().error(f" {rel}: {err}")
|
||||
raise RuntimeError(f"{len(errors)} 个文件翻译失败,已终止编译")
|
||||
else:
|
||||
for idx, (i, src_path, out_path, sha1, msm) in enumerate(need_translate):
|
||||
rel = os.path.relpath(src_path, self.src_root)
|
||||
@@ -470,6 +473,7 @@ class Phase2Translator:
|
||||
except Exception as e:
|
||||
_vlog().error(f" {rel}: {e}")
|
||||
traceback.print_exc()
|
||||
raise RuntimeError(f"翻译 {rel} 失败,已终止编译: {e}") from e
|
||||
t_end = time.time()
|
||||
_vlog().info(f" 翻译总耗时: {t_end-t_start:.1f}s")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user