fix(generic): 恢复泛型特化发射主路径

补回 _specialize_generic_class 的 deepcopy/改名/字段与方法 T 替换/_EmitClassLlvm 发射逻辑,list[...].__len__ 与 get 正常生成;源码不再支持 CPtr[...],统一写 T*;编译错误只由主进程输出一次。
This commit is contained in:
TermiNexus committed 2026-10-18 19:50:32 +08:00
1 parent 09553401e7
commit 369afb5011
13 files changed
+413 -95

No files matched your search

+85 -55
View File
@@ -27,6 +27,15 @@ from StubGen import PythonToStubConverter
from lib.core.LLVMCG.IRMetadataFix import SanitizeIRMetadata
class ReportedCompileError(RuntimeError):
"""主进程已输出完整编译错误(含源码定位)的终止信号。
该异常只用于"错误已经打印过"的路径:main()/run() 抛出后,
顶层入口(Projectrans.py)不得再次打印错误内容,直接以失败退出码结束,
否则同一错误会在 worker/主进程/顶层各出现一遍。
"""
# PROF 热点:nt._path_isfile 2.6 万次 16.4s(Windows 磁盘 stat ~0.6ms/次)。
# 编译进程内文件状态稳定(obj/ll 一旦生成不删除、不被移动),用模块级缓存
# 避免重复 stat。仅用于"存在性检查"类调用(不用于内容变化的判断)。
@@ -763,11 +772,9 @@ def _parallel_translate_worker(src_path: str, out_path: str, src_root: str, temp
trans._translate_file(src_path, out_path, prebuilt_ModuleSha1Map=prebuilt_ModuleSha1Map, dwarf_enabled=dwarf_enabled)
return ('ok', src_path, '')
except Exception as e:
# fail-fast:只输出单行错误(含源码定位),不打印 Python 内部调用栈
sys.stderr.write(
f"[FATAL] 翻译失败: {src_path}\n{type(e).__name__}: {e}\n")
sys.stderr.flush()
os._exit(88)
# fail-fast:worker 不写 stderr,只把错误(含源码定位)返回主进程;
# 由主进程统一输出一次,避免同一错误打印两遍。
return ('error', src_path, f'{type(e).__name__}: {e}')
def _parallel_translate_batch(batch: list, src_root: str, temp_dir: str, output_dir: str,
@@ -787,7 +794,10 @@ def _parallel_translate_batch(batch: list, src_root: str, temp_dir: str, output_
批内每个文件翻译前从 symbol_table_bytes 反序列化出干净副本,前文件的
符号写入不会残留到后文件(否则跨模块 struct 类型前缀错位)。
返回 (错误列表[(src_path, err)], batch, per_file_times[(src_path, elapsed, rel)])。
错误只回传、不写 stderr:完整错误由主进程统一输出一次(含源码定位)。
"""
_current_src: str = batch[0][1] if batch else ''
per_file_times: list[tuple[str, float, str]] = []
try:
trans = Phase2Translator(src_root, temp_dir, output_dir,
compile_cmd='llc', include_dirs=include_dirs,
@@ -830,28 +840,21 @@ def _parallel_translate_batch(batch: list, src_root: str, temp_dir: str, output_
trans._build_shared_symbol_data()
batch_errors: list = []
per_file_times: list[tuple[str, float, str]] = []
for _i, src_path, out_path, _sha1, msm in batch:
_f0: float = time.time()
try:
# 每文件前重置 SymbolTable 为干净副本(防跨文件污染)
if symbol_table_bytes is not None:
trans._shared_symbol_table = pickle.loads(symbol_table_bytes)
trans._shared_symbol_table.translator = None
trans._translate_file(src_path, out_path, prebuilt_ModuleSha1Map=msm, dwarf_enabled=dwarf_enabled)
per_file_times.append((src_path, time.time() - _f0, os.path.relpath(src_path, src_root)))
except Exception as e:
# fail-fast(2026-08-18):任意文件编译出错立即终止整个编译进程,
# 不再收集错误继续翻译批内剩余文件(避免浪费大量时间)。主进程
# 收到 BrokenProcessPool/future 异常后取消全部剩余批次退出。
sys.stderr.write(
f"[FATAL] 文件翻译失败: {src_path}\n"
f"{type(e).__name__}: {e}\n")
sys.stderr.flush()
os._exit(88)
_current_src = src_path
# 每文件前重置 SymbolTable 为干净副本(防跨文件污染)
if symbol_table_bytes is not None:
trans._shared_symbol_table = pickle.loads(symbol_table_bytes)
trans._shared_symbol_table.translator = None
trans._translate_file(src_path, out_path, prebuilt_ModuleSha1Map=msm, dwarf_enabled=dwarf_enabled)
per_file_times.append((src_path, time.time() - _f0, os.path.relpath(src_path, src_root)))
return (batch_errors, batch, per_file_times)
except Exception as e:
return ([(item[1], f'{type(e).__name__}: {e}') for item in batch], batch, [])
# fail-fast:任意文件编译出错立即终止本批(不再翻译批内剩余文件)。
# worker 不写 stderr —— 错误(含源码定位)随返回值交给主进程统一输出一次。
_src: str = _current_src or (batch[0][1] if batch else '')
return ([(_src, f'{type(e).__name__}: {e}')], batch, per_file_times)
# ==================================================================
@@ -1709,7 +1712,6 @@ class Phase2Translator:
_vlog().warning(f"符号表序列化失败({e}),worker将自行构建")
shared_pickle_path = ''
errors = []
# 复用 self._executor_pool:避免局部变量 GC 时 finalizer 阻塞 ~99s
# (join_executor_internals 在 GC 时同步等待 worker 退出队列)
if self._executor_pool is None:
@@ -1770,17 +1772,11 @@ class Phase2Translator:
bidx, rel_first, bcount = futures[future]
try:
batch_errors, batch, per_file_times = future.result()
for src, elapsed, rel in per_file_times:
done_files += 1
_vlog().info(f" [{done_files}/{total_files}] {rel} ({elapsed:.1f}s)")
for src, err in batch_errors:
rel = os.path.relpath(src, self.src_root)
_vlog().error(f" {rel}: {err}")
errors.append((rel, err))
_vlog().info(f" [批次完成] {bcount} 文件, 首个 {rel_first}")
except ReportedCompileError:
raise
except Exception as e:
# fail-fast:worker 已 os._exit(文件编译出错)或进程池
# 已 broken → 立即取消全部剩余批次并终止编译,不空等。
# fail-fast:进程池已 broken(worker 异常退出)→ 取消
# 全部剩余批次并终止编译,不空等。
_vlog().error(f" 批次 {bidx} 异常(worker 已终止): {e}")
for fut in futures:
if not fut.done():
@@ -1788,8 +1784,26 @@ class Phase2Translator:
if self._executor_pool is not None:
self._executor_pool.shutdown(wait=False, cancel_futures=True)
self._executor_pool = None
raise RuntimeError(
f"批次 {bidx} worker 异常终止,编译立即停止: {e}")
raise ReportedCompileError(
f"批次 {bidx} worker 异常终止,编译立即停止") from None
if batch_errors:
# worker 只回传错误、不写 stderr:完整错误(含源码定位)
# 只在这里输出一次,随后立即终止(fail-fast)。
src, err = batch_errors[0]
rel = os.path.relpath(src, self.src_root) if src else rel_first
_vlog().error(f"\n文件翻译失败: {rel}\n{err}")
for fut in futures:
if not fut.done():
fut.cancel()
if self._executor_pool is not None:
self._executor_pool.shutdown(wait=False, cancel_futures=True)
self._executor_pool = None
raise ReportedCompileError(
f"{rel} 翻译失败,编译立即停止") from None
for src, elapsed, rel in per_file_times:
done_files += 1
_vlog().info(f" [{done_files}/{total_files}] {rel} ({elapsed:.1f}s)")
_vlog().info(f" [批次完成] {bcount} 文件, 首个 {rel_first}")
except Exception:
# 异常时立即清理,正常流程不 shutdown(留给 includes 阶段复用)
if self._executor_pool is not None:
@@ -1797,11 +1811,6 @@ class Phase2Translator:
self._executor_pool = None
raise
if errors:
_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)
@@ -1811,9 +1820,10 @@ class Phase2Translator:
t1 = time.time()
_vlog().info(f" [{idx+1}/{len(need_translate)}] {rel} ({t1-t0:.1f}s)")
except Exception as e:
_vlog().error(f" {rel}: {e}")
traceback.print_exc()
raise RuntimeError(f"翻译 {rel} 失败,已终止编译: {e}") from e
# 单点输出:完整错误(含源码定位)只打印这一次
_vlog().error(f"\n文件翻译失败: {rel}\n{type(e).__name__}: {e}")
raise ReportedCompileError(
f"翻译 {rel} 失败,已终止编译") from None
t_end = time.time()
_vlog().info(f" 翻译总耗时: {t_end-t_start:.1f}s")
@@ -3841,13 +3851,8 @@ class Phase2Translator:
done_count += 1
try:
status, _, err = future.result()
if status == 'ok':
_vlog().info(f" [{done_count}/{len(need_translate)}] 翻译完成: {module_name}.py")
else:
# fail-fast:文件编译出错立即终止(worker 已 os._exit,
# 此处兜底处理残留的 error 返回)
raise RuntimeError(
f"include 翻译失败,立即终止: {module_name}.py: {err}")
except ReportedCompileError:
raise
except Exception as e:
_vlog().error(f" {module_name}.py: {e}")
for f2 in in_progress:
@@ -3855,8 +3860,21 @@ class Phase2Translator:
if self._executor_pool is not None:
self._executor_pool.shutdown(wait=False, cancel_futures=True)
self._executor_pool = None
raise RuntimeError(
f"include worker 异常终止,编译立即停止: {e}")
raise ReportedCompileError(
"include worker 异常终止,编译立即停止") from None
if status == 'ok':
_vlog().info(f" [{done_count}/{len(need_translate)}] 翻译完成: {module_name}.py")
else:
# worker 只回传错误、不写 stderr:完整错误(含源码定位)
# 只在这里输出一次,随后立即终止(fail-fast)。
_vlog().error(f"\n文件翻译失败: {module_name}.py\n{err}")
for f2 in in_progress:
f2.cancel()
if self._executor_pool is not None:
self._executor_pool.shutdown(wait=False, cancel_futures=True)
self._executor_pool = None
raise ReportedCompileError(
"include 翻译失败,编译立即停止") from None
except Exception:
if self._executor_pool is not None:
self._executor_pool.shutdown(wait=False, cancel_futures=True)
@@ -3871,12 +3889,24 @@ class Phase2Translator:
for item in need_translate:
src_path, sha1, module_name, ll_path, obj_path, precompiled_obj = item
_vlog().info(f" 翻译: {module_name}.py -> {sha1}.ll")
self._translate_file(src_path, ll_path, prebuilt_ModuleSha1Map=include_ModuleSha1Map, dwarf_enabled=self.traceback_enabled)
try:
self._translate_file(src_path, ll_path, prebuilt_ModuleSha1Map=include_ModuleSha1Map, dwarf_enabled=self.traceback_enabled)
except Exception as e:
# 单点输出:完整错误(含源码定位)只打印这一次
_vlog().error(f"\n文件翻译失败: {module_name}.py\n{type(e).__name__}: {e}")
raise ReportedCompileError(
"include 翻译失败,编译立即停止") from None
else:
item = need_translate[0]
src_path, sha1, module_name, ll_path, obj_path, precompiled_obj = item
_vlog().info(f" 翻译: {module_name}.py -> {sha1}.ll")
self._translate_file(src_path, ll_path, prebuilt_ModuleSha1Map=include_ModuleSha1Map, dwarf_enabled=self.traceback_enabled)
try:
self._translate_file(src_path, ll_path, prebuilt_ModuleSha1Map=include_ModuleSha1Map, dwarf_enabled=self.traceback_enabled)
except Exception as e:
# 单点输出:完整错误(含源码定位)只打印这一次
_vlog().error(f"\n文件翻译失败: {module_name}.py\n{type(e).__name__}: {e}")
raise ReportedCompileError(
"include 翻译失败,编译立即停止") from None
# ========== 阶段2:串行后处理(dso_local + stub_decls 注入)==========
compile_tasks = [] # [(dso_ll_path, obj_path, src_path, sha1, module_name, ll_path, precompiled_obj, ll_content)]