diff --git a/App/lib/core/Phase2.vp b/App/lib/core/Phase2.vp index 3e0dd37..8b51a40 100644 --- a/App/lib/core/Phase2.vp +++ b/App/lib/core/Phase2.vp @@ -980,6 +980,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, # 不再全量收集 includes.binary 下的所有 .obj if reachable_set is not None and reachable_count > 0: ibd_len_pc: t.CSizeT = string.strlen(includes_binary_dir) + td_len_pc: t.CSizeT = string.strlen(temp_dir) ri_pc: int for ri_pc in range(reachable_count): rsha1_pc: str = reachable_set + t.CSizeT(ri_pc) * 17 @@ -1001,10 +1002,13 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, fop_pos += op_len_pc final_obj_paths[fop_pos] = '\0' else: - fb_pc: str = VLogger.fmt_buf() - if fb_pc is not None: - viperlib.snprintf(fb_pc, 1024, "可达模块 .obj 缺失: %s", obj_path_pc) - VLogger.error(fb_pc, "PhaseC") + # .obj 缺失:纯声明模块(text.ll 无 define 行,如 stdio.vp 全 pass) + # 无需 .obj,静默跳过;实现文件缺失才是编译产物异常(fail-fast) + if StubMerger.IsDeclOnlyModule(temp_dir, td_len_pc, rsha1_pc) != 1: + fb_pc: str = VLogger.fmt_buf() + if fb_pc is not None: + viperlib.snprintf(fb_pc, 1024, "可达模块 .obj 缺失: %s", obj_path_pc) + VLogger.error(fb_pc, "PhaseC") stdlib.free(obj_path_pc) final_obj_paths[fop_pos] = '\0' diff --git a/App/lib/core/StubMerger.vp b/App/lib/core/StubMerger.vp index f9c7439..4d298e4 100644 --- a/App/lib/core/StubMerger.vp +++ b/App/lib/core/StubMerger.vp @@ -729,6 +729,61 @@ def LookupSha1RelPath(sha1: str) -> str: return None +# ============================================================ +# IsDeclOnlyModule - 判断模块是否为纯声明文件 +# +# 读取 {temp_dir}/{sha1}.text.ll,扫描是否含 "define " 行: +# 返回 1 = 纯声明(无函数体,如 stdio.vp 全 pass),无需 .obj +# 返回 0 = 实现文件(含 define 函数体) +# 返回 -1 = text.ll 不存在/无法判断 +# +# 与 Phase B+(Phase2.vp 编译缺失 includes 时)的内联判断逻辑一致, +# 供 Phase C 链接时区分"声明文件无需 .obj"与"实现文件缺失"。 +# ============================================================ +def IsDeclOnlyModule(temp_dir: str, td_len: t.CSizeT, sha1: str) -> int: + """判断模块是否为纯声明文件(text.ll 无 define 行)""" + if temp_dir is None or sha1 is None: + return -1 + tpath: str = _sliced_path(temp_dir, td_len, sha1, "text.ll") + if tpath is None: + return -1 + tf_d: fileio.File | t.CPtr = fileio.File(tpath, fileio.MODE.R) + stdlib.free(tpath) + if tf_d.closed: + return -1 + tbuf_d: bytes = stdlib.malloc(STUB_READ_BUF_SIZE) + tbr_d: t.CInt64T = 0 + if tbuf_d is not None: + tbr_d = tf_d.read_all(tbuf_d, STUB_READ_BUF_SIZE) + tf_d.close() + if tbuf_d is None: + return -1 + if tbr_d <= 0: + # 空 text.ll:无函数体,纯声明 + stdlib.free(tbuf_d) + return 1 + if tbr_d < STUB_READ_BUF_SIZE: + tbuf_d[tbr_d] = '\0' + else: + tbuf_d[STUB_READ_BUF_SIZE - 1] = '\0' + # 逐行扫描:找 "define " 行即为实现文件 + tpos_d: t.CSizeT = 0 + while tpos_d < tbr_d: + tls_d: t.CSizeT = tpos_d + while tpos_d < tbr_d: + if tbuf_d[tpos_d] == '\n': + break + tpos_d += 1 + tll_d: t.CSizeT = tpos_d - tls_d + if tpos_d < tbr_d: + tpos_d += 1 + if tll_d >= 7 and string.strncmp(tbuf_d + tls_d, "define ", 7) == 0: + stdlib.free(tbuf_d) + return 0 + stdlib.free(tbuf_d) + return 1 + + # ============================================================ # _IsFuncDeclaredOrDefined - 检查 out_buf 中是否已有函数的 declare/define #