自举实验失败,C1 编译 Test 成功,暂未达到自举收敛点

This commit is contained in:
2026-07-30 15:44:32 +08:00
parent 68481a5a7f
commit 32af3f93fa
22 changed files with 2158 additions and 442 deletions

View File

@@ -19,6 +19,7 @@ import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.Handles.HandlesImports as HandlesImports
import lib.core.Handles.HandlesStruct as HandlesStruct
import lib.core.BuildPipeline as BuildPipeline
import lib.core.IncludesScanner as IncludesScanner
import lib.core.StubMerger as StubMerger
import lib.Projectrans.Utils as Utils
import lib.Projectrans.Config as Config
@@ -207,6 +208,19 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
BuildPipeline.ensure_dir(temp_dir)
BuildPipeline.ensure_dir(output_dir)
# === 0.5 扫描 includes 目录填充 Sha1Store消除 Phase1 依赖)===
# Phase2 不依赖 Phase1 的副作用:如果 Sha1Store 为空Phase1 未运行或提前返回),
# 自行扫描 includes 目录并填充 Sha1Store确保后续"编译缺失 includes"逻辑能正常工作
if includes_dir is not None:
if StubMerger.GetSha1StoreCount() == 0:
inc_scan_result: IncludesScanner.ScanResult | t.CPtr = IncludesScanner.scan_includes(mb, includes_dir)
if inc_scan_result is not None:
inc_filled: int = StubMerger.PopulateSha1MapStore(inc_scan_result)
fb_inc: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb_inc is not None:
viperlib.snprintf(fb_inc, 1024, "Phase2 自主填充 includes Sha1Store: %d", inc_filled)
VLogger.info(fb_inc, "project")
# 追加 App 源文件 SHA1 到 _sha1_map.txt人类可读输出程序内部不读取此文件
# 格式: {sha1}:{rel_path}\n (保留目录结构,如 lib/core/VLogger.py
# 同步追加到内存存储器AppendToSha1MapStore供跨模块 CDefine 查找)
@@ -296,10 +310,62 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
# === 2. Phase A: 为每个文件生成 stub + text ===
# app_deps_buf: 收集所有 App 源文件的直接依赖模块名(空格分隔)
# 供 Phase B+ 的 _BuildReachableSha1Set 作为初始 worklist实现按需编译
# 声明在 if 块外,确保 Phase B+ 和清理代码能访问
APP_DEPS_BUF_SIZE: t.CSizeT = 8192
app_deps_buf: bytes = None
app_deps_len: t.CSizeT = 0
if do_phase1 != 0:
if log is not None:
log.banner("Phase A: 生成 stub + text")
# === Phase A-pre-inc: includes 预注册 struct + 依赖填充 ===
# 必须在 App 源文件预注册之前执行App 源文件strict_mode=1可能继承
# 或引用 includes 中的 struct如 ast.AST。若 includes struct 未注册,
# App 源文件翻译会失败 → .obj 缺失 → undefined reference。
# 同时填充 PopulateIncludesDeps供 Phase B+ 的 _BuildReachableSha1Set 使用。
# 多遍扫描:解决跨文件继承问题(如 Constant(AST) 在 AST 未注册时被跳过)。
if includes_dir is not None and includes_binary_dir is not None:
store_count_inc_pre: int = StubMerger.GetSha1StoreCount()
if store_count_inc_pre > 0:
store_sha1_inc_pre: bytes | t.CPtr = StubMerger.GetSha1StoreArrPtr()
store_rel_inc_pre: bytes | t.CPtr = StubMerger.GetSha1StoreRelArrPtr()
inc_dir_len_pre: t.CSizeT = string.strlen(includes_dir)
deps_filled_pre: int = 0
PHASE_A_INC_MAX_PASSES: t.CInt = 3
for pass_inc_i in range(PHASE_A_INC_MAX_PASSES):
struct_count_before_inc: int = HandlesStruct.get_struct_count()
for si_inc in range(store_count_inc_pre):
inc_sha1_pre: str = store_sha1_inc_pre + t.CSizeT(si_inc) * 17
inc_rel_pre: str = store_rel_inc_pre + t.CSizeT(si_inc) * StubMerger.MAX_REL_PATH_LEN
if inc_rel_pre is None or inc_rel_pre[0] == '\0':
continue
rel_len_pre: t.CSizeT = string.strlen(inc_rel_pre)
full_path_pre: bytes = stdlib.malloc(inc_dir_len_pre + rel_len_pre + 2)
if full_path_pre is None:
continue
viperlib.snprintf(full_path_pre, inc_dir_len_pre + rel_len_pre + 2,
"%s/%s", includes_dir, inc_rel_pre)
pkg_inc_pre: str = HandlesImports.compute_package_from_relpath(mb, inc_rel_pre)
tr_inc_pre: HandlesTranslator.Translator | t.CPtr = BuildPipeline.TranslateFileGetTrans(
mb, full_path_pre, inc_sha1_pre, pkg_inc_pre, 1)
stdlib.free(full_path_pre)
if tr_inc_pre is not None:
# 依赖填充只在第一遍执行PopulateIncludesDeps 内部有去重)
if pass_inc_i == 0:
if tr_inc_pre._imported_modules is not None:
if StubMerger.PopulateIncludesDeps(inc_sha1_pre, tr_inc_pre._imported_modules) == 0:
deps_filled_pre = deps_filled_pre + 1
struct_count_after_inc: int = HandlesStruct.get_struct_count()
# 收敛检查:本遍没有新结构体注册 → 所有类已注册
if struct_count_after_inc == struct_count_before_inc:
break
fb_inc_pre: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb_inc_pre is not None:
viperlib.snprintf(fb_inc_pre, 1024, "includes 预注册 + 依赖填充: %d/%d", deps_filled_pre, store_count_inc_pre)
VLogger.info(fb_inc_pre, "prePhaseA")
# === Phase A-pre: 预注册所有源文件的 struct/enum/union ===
# 解决循环引用问题circ_a 翻译时需要知道 circ_b.ClassB 的 struct 定义
# 仅注册 struct/enum/uniondeclare_only=1不翻译方法体
@@ -334,6 +400,18 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
PHASE_A_IR_SIZE: t.CSizeT = 1048576
td_len_pa: t.CSizeT = string.strlen(temp_dir)
# 设置 temp 目录路径,使 HandlesExprCall 能读取依赖模块的 text.ll
# 用于跨模块方法返回类型查找(避免指针返回值被截断为 i32
HandlesExprCall.SetTempDir(temp_dir)
# app_deps_buf: 收集所有 App 源文件的直接依赖模块名(空格分隔)
# 供 Phase B+ 的 _BuildReachableSha1Set 作为初始 worklist 使用
# 声明已在 if 块外,此处仅赋值
app_deps_buf = stdlib.malloc(APP_DEPS_BUF_SIZE)
app_deps_len = 0
if app_deps_buf is not None:
app_deps_buf[0] = '\0'
for i in range(file_count):
ea: t.CUInt64T = t.CUInt64T(entries) + i * entry_size
ent: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea, t.CPtr))
@@ -394,8 +472,21 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
df_a.close()
stdlib.free(deps_path_a)
# 收集 _imported_modules 到 app_deps_buf供 Phase B+ 按需编译)
if tr_a._imported_modules is not None and app_deps_buf is not None:
im_len_a: t.CSizeT = string.strlen(tr_a._imported_modules)
if im_len_a > 0 and app_deps_len + im_len_a + 1 < APP_DEPS_BUF_SIZE:
if app_deps_len > 0:
app_deps_buf[app_deps_len] = ' '
app_deps_len = app_deps_len + 1
string.strcpy(app_deps_buf + app_deps_len, tr_a._imported_modules)
app_deps_len = app_deps_len + im_len_a
app_deps_buf[app_deps_len] = '\0'
if do_phase2 == 0:
VLogger.info("Phase A 完成(仅 stub 生成)", "project")
if app_deps_buf is not None:
stdlib.free(app_deps_buf)
stdlib.free(entries)
return 0
@@ -404,8 +495,11 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
if log is not None:
log.banner("Phase B: 编译 .obj")
# 收集 .obj 路径
OBJ_PATHS_SIZE: t.CSizeT = 8192
# 收集 .obj 路径(增大到 64KB避免 includes .obj 路径溢出导致链接丢失符号)
# reachable_set/reachable_count: Phase B+ 填充Phase C 用于按需链接
reachable_set: bytes = None
reachable_count: int = 0
OBJ_PATHS_SIZE: t.CSizeT = 65536
obj_paths: bytes = stdlib.malloc(OBJ_PATHS_SIZE)
if obj_paths is None:
return 1
@@ -457,6 +551,11 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
sys.exit(1)
compiled_count += 1
# 显示编译完成的文件
fb_comp: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb_comp is not None:
viperlib.snprintf(fb_comp, 1024, "[%d/%d] 编译完成: %s", i + 1, file_count, ent.Path)
VLogger.info(fb_comp, "PhaseB")
# 构造 .obj 路径,检测是否是 main 模块test_main.py 或 main.py
od_len: t.CSizeT = string.strlen(output_dir)
is_main_mod: int = 0
@@ -497,39 +596,85 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
stdlib.free(entries)
return 1
# === 3.5 编译缺失的 includes 文件 ===
# includes.binary 可能缺少某些 includes .obj如 testcheck.py
# 这些文件被用户项目导入但未被 TransPyV 自身依赖Projectrans.py 未编译它们
# 检测并编译缺失的 includes 文件到 output_dir加入链接命令。
# 数据来源StubMerger 全局内存存储器(不读取 _sha1_map.txt 文件)
# === 3.5 编译缺失的 includes 文件(按依赖图按需编译)===
# 只编译被 App 源文件直接/间接引用的 includes 文件(可达集合
# 而非遍历整个 includes 目录。通过 _BuildReachableSha1Set 构建依赖图
if includes_dir is not None and includes_binary_dir is not None:
inc_compiled: int = 0
td_len_mi: t.CSizeT = string.strlen(temp_dir)
# 诊断日志:确认 Phase B+ 入口和 Sha1Store 状态
fb_pbp_enter: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb_pbp_enter is not None:
viperlib.snprintf(fb_pbp_enter, 1024,
"PhaseB+ 入口: store_count=%d includes_binary_dir=%s",
StubMerger.GetSha1StoreCount(), includes_binary_dir)
VLogger.info(fb_pbp_enter, "PhaseB+")
# 创建 includes_binary_dir 目录(确保 .obj 能写入和 collect_obj_files 能扫描)
BuildPipeline.ensure_dir(includes_binary_dir)
# declare_only 预处理已移至 Phase A 之前Phase A-pre-inc
# 确保 Phase A 翻译 App 源文件时 includes struct 已注册。
# PopulateIncludesDeps 已在 Phase A-pre-inc 中填充。
# 构建可达 SHA1 集合(依赖图遍历)
# app_deps_buf 为 None 时回退到扫描 source_dir非递归
REACHABLE_SET_SIZE: t.CSizeT = t.CSizeT(StubMerger.MAX_INCLUDES_SHA1) * 17
reachable_set: bytes = stdlib.malloc(REACHABLE_SET_SIZE)
reachable_count: int = 0
if reachable_set is not None:
string.memset(reachable_set, 0, REACHABLE_SET_SIZE)
reachable_count = StubMerger._BuildReachableSha1Set(
mb, source_dir, temp_dir, reachable_set, app_deps_buf)
fb_reach: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb_reach is not None:
viperlib.snprintf(fb_reach, 1024, "可达 includes SHA1: %d", reachable_count)
VLogger.info(fb_reach, "PhaseB+")
# fast-fail: 可达集合构建失败(依赖未找到),永不回退,直接终止
if reachable_count < 0:
VLogger.error("可达集合构建失败fast-fail终止编译", "PhaseB+")
return 1
else:
VLogger.error("reachable_set 分配失败,终止编译", "PhaseB+")
return 1
# 释放 app_deps_buf已构建完 reachable_set不再需要
if app_deps_buf is not None:
stdlib.free(app_deps_buf)
app_deps_buf = None
# 从全局存储器获取 SHA1/模块名/rel_path 数组(直接访问器,避免 box 解引用问题)
store_count_mi: int = StubMerger.GetSha1StoreCount()
if store_count_mi > 0:
store_sha1_arr_mi: bytes | t.CPtr = StubMerger.GetSha1StoreArrPtr()
store_mod_arr_mi: bytes | t.CPtr = StubMerger.GetSha1StoreModArrPtr()
store_rel_arr_mi: bytes | t.CPtr = StubMerger.GetSha1StoreRelArrPtr()
for si_mi in range(store_count_mi):
# 获取当前条目的 SHA1 和 rel_path
inc_sha1_mi: str = store_sha1_arr_mi + t.CSizeT(si_mi) * 17
inc_rel_mi: str = store_rel_arr_mi + t.CSizeT(si_mi) * StubMerger.MAX_REL_PATH_LEN
# 检查 .obj 是否已存在于 includes.binary
# 按需编译过滤:只处理 reachable_set 中的 includes
# 永不回退reachable_count > 0 时严格按可达集合过滤
if reachable_count > 0:
if StubMerger._is_in_sha1_set(inc_sha1_mi, reachable_set, reachable_count) == 0:
continue
# 检查 .obj 是否已存在于 includes.binary切片子目录
ibd_len_mi: t.CSizeT = string.strlen(includes_binary_dir)
check_pat_mi: bytes = stdlib.malloc(ibd_len_mi + 35)
if check_pat_mi is None:
continue
viperlib.snprintf(check_pat_mi, ibd_len_mi + 35, "%s/%s*.obj", includes_binary_dir, inc_sha1_mi)
check_pat_mi: str = StubMerger._sliced_path(includes_binary_dir, ibd_len_mi, inc_sha1_mi, "obj")
check_fd_mi: w32.win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(w32.win32file.WIN32_FIND_DATAA.__sizeof__())
obj_exists_mi: int = 0
if check_fd_mi is not None:
if check_pat_mi is not None and check_fd_mi is not None:
string.memset(check_fd_mi, 0, w32.win32file.WIN32_FIND_DATAA.__sizeof__())
check_h_mi: w32.win32base.HANDLE = w32.win32file.FindFirstFileA(check_pat_mi, check_fd_mi)
if check_h_mi != w32.win32base.INVALID_HANDLE_VALUE:
w32.win32file.FindClose(check_h_mi)
obj_exists_mi = 1
if obj_exists_mi != 0:
if check_pat_mi is not None:
stdlib.free(check_pat_mi)
stdlib.free(check_fd_mi)
continue
# .obj 不存在,需要编译
@@ -538,6 +683,9 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
inc_dir_len_mi: t.CSizeT = string.strlen(includes_dir)
src_fp_mi: bytes = stdlib.malloc(inc_dir_len_mi + 1 + rel_path_len_mi + 1)
if src_fp_mi is None:
stdlib.free(check_pat_mi)
if check_fd_mi is not None:
stdlib.free(check_fd_mi)
continue
viperlib.snprintf(src_fp_mi, inc_dir_len_mi + 1 + rel_path_len_mi + 1,
"%s/%s", includes_dir, inc_rel_mi)
@@ -558,7 +706,9 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
tbuf_mi[tbr_mi] = '\0'
else:
tbuf_mi[StubMerger.STUB_READ_BUF_SIZE - 1] = '\0'
# 逐行扫描: 找 define 行中含 @" 的(混淆函数名)
# 逐行扫描: 找 define 行即为实现文件
# (不再要求行中含 @",因为 memhub 等模块的函数名
# 如 @ca40915f11b8b6ad._align_up 不带引号但仍是实现文件)
tpos_mi: t.CSizeT = 0
while tpos_mi < tbr_mi:
tls_mi: t.CSizeT = tpos_mi
@@ -570,21 +720,20 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
if tpos_mi < tbr_mi:
tpos_mi += 1
if tll_mi >= 7 and string.strncmp(tbuf_mi + tls_mi, "define ", 7) == 0:
# 临时在行尾加 \0 供 strstr 使用
saved_mi: t.CChar = tbuf_mi[tls_mi + tll_mi]
tbuf_mi[tls_mi + tll_mi] = '\0'
if string.strstr(tbuf_mi + tls_mi, "@\"") is not None:
tbuf_mi[tls_mi + tll_mi] = saved_mi
is_decl_mi = 0
break
tbuf_mi[tls_mi + tll_mi] = saved_mi
is_decl_mi = 0
break
stdlib.free(tbuf_mi)
tf_mi.close()
stdlib.free(tpath_mi)
# is_decl_mi == -1: text.ll 不存在Phase1 未翻译此文件,可能不被项目导入
# is_decl_mi == -1: text.ll 不存在Phase1 未翻译此文件)
# is_decl_mi == 1: 声明文件(仅 declare 无 define
# 两种情况都跳过:不编译未被 Phase1 翻译的 includes 文件
if is_decl_mi != 0:
# is_decl_mi == 0: 实现文件(有 define
# 只跳过声明文件is_decl_mi == 1text.ll 不存在时继续,让后续翻译逻辑处理
if is_decl_mi == 1:
stdlib.free(check_pat_mi)
if check_fd_mi is not None:
stdlib.free(check_fd_mi)
stdlib.free(src_fp_mi)
continue
# 尝试 BuildCombinedIRstub/text 应已由 Phase1 生成)
@@ -633,19 +782,43 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
inc_combined_len_mi = StubMerger.BuildCombinedIR(temp_dir, inc_sha1_mi, inc_combined_mi, COMBINED_IR_SIZE)
if inc_combined_len_mi == 0:
# 诊断:检查 stub.ll/text.ll 是否存在
diag_stub: str = StubMerger._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "stub.ll")
diag_text: str = StubMerger._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "text.ll")
diag_stub_ex: int = 0
diag_text_ex: int = 0
if diag_stub is not None:
df_diag_s: fileio.File | t.CPtr = fileio.File(diag_stub, fileio.MODE.R)
if not df_diag_s.closed:
diag_stub_ex = 1
df_diag_s.close()
stdlib.free(diag_stub)
if diag_text is not None:
df_diag_t: fileio.File | t.CPtr = fileio.File(diag_text, fileio.MODE.R)
if not df_diag_t.closed:
diag_text_ex = 1
df_diag_t.close()
stdlib.free(diag_text)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "BuildCombinedIR 失败: %s", src_fp_mi)
viperlib.snprintf(fb, 1024,
"BuildCombinedIR 失败: %s (stub=%d text=%d is_decl=%d)",
src_fp_mi, diag_stub_ex, diag_text_ex, is_decl_mi)
VLogger.error(fb, "PhaseB+")
if inc_combined_mi is not None:
stdlib.free(inc_combined_mi)
stdlib.free(check_pat_mi)
if check_fd_mi is not None:
stdlib.free(check_fd_mi)
stdlib.free(src_fp_mi)
continue
# 编译为 .obj
# 编译为 .obj(输出到 includes_binary_dir与存在性检查和链接收集一致
inc_cret_mi: int = BuildPipeline.compile_module_to_obj(
inc_combined_mi, inc_combined_len_mi, temp_dir, output_dir, inc_sha1_mi,
inc_combined_mi, inc_combined_len_mi, temp_dir, includes_binary_dir, inc_sha1_mi,
cc_cmd, cc_flags)
stdlib.free(inc_combined_mi)
if inc_cret_mi != 0:
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
@@ -654,10 +827,16 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
sys.exit(1)
inc_compiled += 1
# 显示编译完成的 includes 文件
fb_inc_comp: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb_inc_comp is not None:
viperlib.snprintf(fb_inc_comp, 1024, "[includes %d] 编译完成: %s", inc_compiled, src_fp_mi)
VLogger.info(fb_inc_comp, "PhaseB+")
# 添加到 obj_paths (切片路径,从 includes_binary_dir 取)
ibd_len_mi2: t.CSizeT = string.strlen(includes_binary_dir)
inc_obj_sliced: str = StubMerger._sliced_path(includes_binary_dir, ibd_len_mi2, inc_sha1_mi, "obj")
# 添加到 obj_paths (切片路径)
od_len_mi: t.CSizeT = string.strlen(output_dir)
inc_obj_sliced: str = StubMerger._sliced_path(output_dir, od_len_mi, inc_sha1_mi, "obj")
if inc_obj_sliced is not None:
inc_obj_len: t.CSizeT = string.strlen(inc_obj_sliced)
if obj_pos + inc_obj_len + 2 < OBJ_PATHS_SIZE:
@@ -669,11 +848,25 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
obj_paths[obj_pos] = '\0'
stdlib.free(inc_obj_sliced)
# 释放本次迭代的临时内存
if check_pat_mi is not None:
stdlib.free(check_pat_mi)
if check_fd_mi is not None:
stdlib.free(check_fd_mi)
stdlib.free(src_fp_mi)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "编译缺失 includes: %d", inc_compiled)
VLogger.info(fb, "PhaseB+")
# 释放 app_deps_bufPhase B+ 块未执行时此处兜底释放)
if app_deps_buf is not None:
stdlib.free(app_deps_buf)
app_deps_buf = None
# === 4. Phase C: 链接所有 .obj → .exe ===
if log is not None:
log.banner("Phase C: 链接")
@@ -708,6 +901,18 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
final_obj_paths[fop_pos] = '\0'
obj_paths_len: t.CSizeT = fop_pos
# 暂时禁用按图链接:始终全量链接 includes.binary 中的 .obj
# 按图链接在 --clean 后 deps.txt 缺失时无法正确工作(跨包传递依赖丢失)
# 按图编译Phase B+)仍保留:只编译 reachable_set 中的缺失 includes
# TODO: 待 deps.txt 生成机制完善后(如 Phase A 翻译所有 includes 的 imports重新启用按图链接
# 释放 reachable_setPhase B+ 已使用完毕)
if reachable_set is not None:
stdlib.free(reachable_set)
reachable_set = None
# 始终传 includes_binary_dir 给 link_objs_to_exe由其全量收集 .obj
lret: int = BuildPipeline.link_objs_to_exe(
final_obj_paths, obj_paths_len,
linker_cmd, linker_flags, exe_path,