修正了种子编译器的错误

This commit is contained in:
2026-07-22 21:55:36 +08:00
parent 135aa05485
commit ca7c2120b8
1185 changed files with 12056 additions and 2673 deletions

View File

@@ -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_buildtemp 和 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']]

View File

@@ -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]:

View File

@@ -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)
# 写入依赖签名 sidecartemp_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}")

View File

@@ -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")