fix(gen): Phase B+ 与链接跳过 App 源文件,修复链接 duplicate symbol

Scene3D 的 App 源文件 gfx.vp 同时被 includes 扫描(includes 目录 ../..
=ViperOS 含 Apps/),且被 main.vp import → 进入可达集合 → Phase B+ 编译
一份 .obj 到 includes_binary,与 Phase A/B 编译到 output/ 的 .obj 同
sha1 同符号(9aab6ff7475c1bbf.Vec3.__init__ 等)→ ld.lld duplicate symbol。

- Phase B+ 编译循环:sha1 命中 App 源文件(entries)则跳过(其 .obj 已由
  Phase A/B 产出到 output/)
- 链接期 reachable obj 收集:同样跳过 App 源文件,避免 includes_binary
  残留同 sha1 .obj 与 output obj 重复入链
This commit is contained in:
TermiNexus committed 2026-08-26 17:23:20 +08:00
1 parent c656cba393
commit 9b56d1d75c
1 file changed
+30
+30
View File
@@ -1323,6 +1323,22 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
# 获取当前条目的 SHA1 和 rel_path
inc_sha1_mi: str = store_sha1_arr_mi + t.CSizeT(ti) * 17
inc_rel_mi: str = store_rel_arr_mi + t.CSizeT(ti) * StubMerger.MAX_REL_PATH_LEN
# 跳过 App 源文件:App 文件可能同时被 includes 扫描(includes
# 目录含 source_dir 上层,如 ViperOS 含 Apps/),且被其他 App
# import(Scene3D 的 gfx.vp)→ 在可达集合中。若 Phase B+ 再编译
# 一份 .obj 到 includes_binary,与 Phase A/B 编译到 output/ 的
# .obj 同 sha1 同符号 → 链接 duplicate symbol
# (9aab6ff7475c1bbf.Vec3.__init__ 等,Scene3D 根因)。
app_skip_mi: int = 0
for ai_mi in range(file_count):
ea_ap: t.CUInt64T = t.CUInt64T(entries) + ai_mi * entry_size
ent_ap: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea_ap, t.CPtr))
if ent_ap is not None and ent_ap.Sha1 is not None:
if string.strcmp(ent_ap.Sha1, inc_sha1_mi) == 0:
app_skip_mi = 1
break
if app_skip_mi != 0:
continue
# 检查 .obj 是否已存在于 includes.binary(切片子目录)
ibd_len_mi: t.CSizeT = string.strlen(includes_binary_dir)
check_pat_mi: str = StubMergerCollect._sliced_path(includes_binary_dir, ibd_len_mi, inc_sha1_mi, "obj")
@@ -1612,6 +1628,20 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
rsha1_pc: str = reachable_set + t.CSizeT(ri_pc) * 17
if rsha1_pc[0] == '\0':
continue
# 跳过 App 源文件:其 .obj 已在 output/(Phase A/B 编译,obj_paths
# 已入 final_obj_paths),若 includes_binary 残留同 sha1 的 .obj
# (上一轮/缓存),收集进来会与 output obj 同符号 → 链接
# duplicate symbol(Scene3D gfx 9aab 根因)。
app_skip_pc: int = 0
for ai_pc in range(file_count):
ea_pc: t.CUInt64T = t.CUInt64T(entries) + ai_pc * entry_size
ent_pc: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea_pc, t.CPtr))
if ent_pc is not None and ent_pc.Sha1 is not None:
if string.strcmp(ent_pc.Sha1, rsha1_pc) == 0:
app_skip_pc = 1
break
if app_skip_pc != 0:
continue
obj_path_pc: str = StubMergerCollect._sliced_path(includes_binary_dir, ibd_len_pc, rsha1_pc, "obj")
if obj_path_pc is not None:
# 检查 .obj 文件是否存在