From 60493d6ae5dbba3fde6e80beae6f710f912ca34e Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sat, 8 Aug 2026 10:31:44 +0800 Subject: [PATCH] =?UTF-8?q?fix(compiler):=20=E6=94=AF=E6=8C=81=E5=A4=9A=20?= =?UTF-8?q?includes=20=E7=9B=AE=E5=BD=95=EF=BC=8C=E4=BF=AE=E5=A4=8D=20G4?= =?UTF-8?q?=20g4node=20=E6=9C=AA=E6=B3=A8=E5=86=8C=20SHA1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Config.vp 解析 includes 数组全部元素为空格分隔 IncludesDirs(首元素仍存 IncludesDir 兼容单目录) - IncludesScanner 逐目录递归扫描合并,FileEntry.IncDir 记录所属 includes 根目录 - StubMerger 存储新增 IncDir 并行数组(MAX_INC_DIR_LEN=512),容量 256→1024 - Phase2 预注册/3.5 按需编译改用逐条目 IncDir 拼接源路径(空串回退 includes_dir) - HandlesImports import 未注册错误显示行内内容 + App 兄弟模块文件豁免 - 新增 TransPyV 自身 project.vpj --- App/lib/Projectrans/Config.vp | 68 ++++++++++++++++++++--- App/lib/core/Handles/HandlesImports.vp | 75 ++++++++++++++++++++++++++ App/lib/core/IncludesScanner.vp | 53 ++++++++++++++---- App/lib/core/Phase2.vp | 23 +++++--- App/lib/core/StubMerger.vp | 38 ++++++++++--- App/main.vp | 6 +-- project.vpj | 29 ++++++++++ 7 files changed, 257 insertions(+), 35 deletions(-) create mode 100644 project.vpj diff --git a/App/lib/Projectrans/Config.vp b/App/lib/Projectrans/Config.vp index 5a10f36..f04c2e9 100644 --- a/App/lib/Projectrans/Config.vp +++ b/App/lib/Projectrans/Config.vp @@ -34,7 +34,8 @@ TargetTriple: str TargetDataLayout: str Sha1SliceLevel: t.CInt # SHA1 切片层数(目录分散等级) StrictMode: t.CInt -IncludesDir: str # includes 目录路径(从 includes 数组首元素) +IncludesDir: str # includes 目录路径(从 includes 数组首元素,兼容单目录场景) +IncludesDirs: str # 全部 includes 目录(空格分隔,多 includes 目录支持) def Load_project_config(path: str) -> int: @@ -49,7 +50,7 @@ def Load_project_config(path: str) -> int: global SourceDir, BuildDir, TempDir, OutputDir, ProjectName, ProjectVersion global CompilerCmd, CompilerFlags, LinkerCmd, LinkerFlags, LinkerOutput global TargetTriple, TargetDataLayout, Sha1SliceLevel, StrictMode - global IncludesDir + global IncludesDir, IncludesDirs if path is None: return 1 @@ -141,14 +142,33 @@ def Load_project_config(path: str) -> int: if sm is not None and sm.is_bool(): StrictMode = 1 if sm.as_bool() else 0 - # 读取 includes 数组(取首个元素作为 includes 目录) + # 读取 includes 数组(支持多目录:首元素存 IncludesDir,全部拼接存 IncludesDirs) includes_val: JsonValue | t.CPtr = root.__getitem__("includes") if includes_val is not None and includes_val.is_array(): arr_len: t.CSizeT = includes_val.__len__() - if arr_len > 0: - first_inc: JsonValue | t.CPtr = includes_val.get_item(0) - if first_inc is not None and first_inc.is_string(): - IncludesDir = first_inc.as_string() + inc_buf: bytes = stdlib.malloc(2048) + if inc_buf is not None: + inc_buf[0] = '\0' + inc_pos: t.CSizeT = 0 + for inc_i in range(arr_len): + inc_item: JsonValue | t.CPtr = includes_val.get_item(inc_i) + if inc_item is None or not inc_item.is_string(): + continue + inc_str: str = inc_item.as_string() + inc_slen: t.CSizeT = string.strlen(inc_str) + if inc_i == 0: + IncludesDir = inc_str + # 拼接进 IncludesDirs(空格分隔) + if inc_pos + inc_slen + 1 < 2048: + if inc_pos > 0: + inc_buf[inc_pos] = ' ' + inc_pos += 1 + string.strcpy(inc_buf + inc_pos, inc_str) + inc_pos += inc_slen + inc_buf[inc_pos] = '\0' + IncludesDirs = inc_buf + if IncludesDir is None: + IncludesDir = inc_buf return 0 @@ -367,7 +387,7 @@ def resolve_paths(project_dir: str) -> int: Returns: 0 表示成功,非 0 表示失败 """ - global SourceDir, BuildDir, TempDir, OutputDir, IncludesDir + global SourceDir, BuildDir, TempDir, OutputDir, IncludesDir, IncludesDirs if project_dir is None: return 1 if _mbuddy is None: @@ -390,6 +410,38 @@ def resolve_paths(project_dir: str) -> int: OutputDir = output_buf # includes 路径可能是 ../includes 形式,需要基于 project_dir 解析 + # IncludesDir(首元素)与 IncludesDirs(全部,空格分隔)逐项规范化 + if IncludesDirs is not None: + id_total: t.CSizeT = string.strlen(IncludesDirs) + id_out: str = _mbuddy.alloc(id_total * 3 + 64) + if id_out is not None: + id_out[0] = '\0' + id_pos: t.CSizeT = 0 + id_i: t.CSizeT = 0 + while id_i < id_total: + while id_i < id_total and IncludesDirs[id_i] == ' ': + id_i += 1 + if id_i >= id_total: + break + id_start: t.CSizeT = id_i + while id_i < id_total and IncludesDirs[id_i] != ' ': + id_i += 1 + seg_len: t.CSizeT = id_i - id_start + if seg_len > 0: + seg_buf: str = _mbuddy.alloc(seg_len + 1) + if seg_buf is not None: + string.strncpy(seg_buf, IncludesDirs + id_start, seg_len) + seg_buf[seg_len] = '\0' + joined: str = _join_path(seg_buf, project_dir) + if joined is not None: + jl: t.CSizeT = string.strlen(joined) + if id_pos > 0: + id_out[id_pos] = ' ' + id_pos += 1 + string.strcpy(id_out + id_pos, joined) + id_pos += jl + id_out[id_pos] = '\0' + IncludesDirs = id_out if IncludesDir is not None: IncludesDir = _join_path(IncludesDir, project_dir) return 0 diff --git a/App/lib/core/Handles/HandlesImports.vp b/App/lib/core/Handles/HandlesImports.vp index 58f0185..8235330 100644 --- a/App/lib/core/Handles/HandlesImports.vp +++ b/App/lib/core/Handles/HandlesImports.vp @@ -7,6 +7,7 @@ import stdlib import viperlib import stdio import sys +import w32.fileio as fileio import lib.core.VLogger as VLogger import lib.core.Handles.HandlesBase as HandlesBase import lib.core.Handles.HandlesTranslator as HT @@ -507,6 +508,27 @@ def _report_import_error(node: ast.AST | t.CPtr, msg: str) -> None: if file_name is None: file_name = "" stdio.printf(' File "%s", line %d\n', file_name, lineno) + # 显示行内内容(读源文件第 lineno 行的实际代码,便于快速定位) + if lineno > 0 and file_name is not None: + f_err: fileio.File | t.CPtr = fileio.File(file_name, fileio.MODE.R) + if not f_err.closed: + line_buf_err: bytes = stdlib.malloc(512) + if line_buf_err is not None: + cur_ln: int = 0 + while cur_ln < lineno: + r_err: LONG = f_err.readline(line_buf_err, 511) + if r_err <= 0: + break + cur_ln += 1 + if cur_ln == lineno: + ll_err: t.CSizeT = string.strlen(line_buf_err) + while ll_err > 0 and (line_buf_err[ll_err - 1] == '\n' or line_buf_err[ll_err - 1] == '\r'): + ll_err -= 1 + line_buf_err[ll_err] = '\0' + stdio.printf(" %s\n", line_buf_err) + break + stdlib.free(line_buf_err) + f_err.close() stdio.printf("ImportError: %s\n", msg) sys.exit(1) @@ -522,6 +544,54 @@ def _report_import_error(node: ast.AST | t.CPtr, msg: str) -> None: # # t/c 是内部库,不参与 includes 扫描,豁免检查。 # ============================================================ +def _is_local_module_file(mod_name: str) -> int: + """当前文件同目录存在 {mod_name}.vp/.py → 项目内兄弟模块 + + 多文件项目(如 Test/G4/App 的 caller1.vp import g4node)的兄弟模块 + 不在 includes 扫描存储器中(SHA1 在项目源文件列表),检查应豁免。 + """ + cur: str = _g_current_file + if cur is None or mod_name is None: + return 0 + cur_len: t.CSizeT = string.strlen(cur) + if cur_len == 0: + return 0 + sep_pos: t.CSizeT = cur_len + while sep_pos > 0: + sep_pos -= 1 + ch: t.CChar = cur[sep_pos] + if ch == '/' or ch == '\\': + sep_pos += 1 + break + mod_len: t.CSizeT = string.strlen(mod_name) + path_len: t.CSizeT = sep_pos + mod_len + 5 + path_buf: bytes = stdlib.malloc(path_len + 1) + if path_buf is None: + return 0 + # .vp 文件 + string.strncpy(path_buf, cur, sep_pos) + path_buf[sep_pos] = '\0' + string.strcat(path_buf, mod_name) + string.strcat(path_buf, ".vp") + fex: fileio.File | t.CPtr = fileio.File(path_buf, fileio.MODE.R) + if not fex.closed: + fex.close() + stdlib.free(path_buf) + return 1 + # .py 文件(Python 模块可能无 .vp) + string.strncpy(path_buf, cur, sep_pos) + path_buf[sep_pos] = '\0' + string.strcat(path_buf, mod_name) + string.strcat(path_buf, ".py") + fex2: fileio.File | t.CPtr = fileio.File(path_buf, fileio.MODE.R) + if not fex2.closed: + fex2.close() + stdlib.free(path_buf) + return 1 + stdlib.free(path_buf) + return 0 + + def _check_import_sha1(node: ast.AST | t.CPtr, mod_name: str) -> int: """导入时校验模块 SHA1 是否已注册,未注册则立即报错退出 @@ -539,6 +609,11 @@ def _check_import_sha1(node: ast.AST | t.CPtr, mod_name: str) -> int: return 1 mod_sha1: str = StubMerger.LookupSha1ByModName(mod_name) if mod_sha1 is None: + # App 兄弟模块豁免:当前文件同目录存在 {mod_name}.vp/.py + # (如 Test/G4/App/caller1.vp import g4node——同项目源文件, + # SHA1 在项目文件列表而非 includes 扫描存储器,不应报错) + if _is_local_module_file(mod_name) != 0: + return 1 err_buf: t.CChar | t.CPtr = stdlib.malloc(256) if err_buf is not None: viperlib.snprintf(err_buf, 256, diff --git a/App/lib/core/IncludesScanner.vp b/App/lib/core/IncludesScanner.vp index 604409d..d194fc6 100644 --- a/App/lib/core/IncludesScanner.vp +++ b/App/lib/core/IncludesScanner.vp @@ -27,7 +27,7 @@ _mbuddy: memhub.MemBuddy | t.CPtr # 文件路径最大长度 MAX_PATH_LEN: t.CDefine = 512 # 单次扫描最大文件数 -MAX_FILES: t.CDefine = 256 +MAX_FILES: t.CDefine = 1024 # ============================================================ @@ -40,6 +40,7 @@ class FileEntry: Sha1: str # SHA1 前16字符(16字节+null) RelPath: str # 相对于 includes 根目录的路径 ModuleName: str # 模块名(如 "ast.parser") + IncDir: str # 所属 includes 根目录(多 includes 目录支持) # ============================================================ @@ -83,8 +84,8 @@ def create_scan_result(pool: memhub.MemBuddy | t.CPtr) -> ScanResult | t.CPtr: def add_file_entry(result: ScanResult | t.CPtr, pool: memhub.MemBuddy | t.CPtr, abs_path: str, rel_path: str, - sha1: str) -> int: - """添加文件条目到扫描结果,返回 0 成功""" + sha1: str, inc_dir: str) -> int: + """添加文件条目到扫描结果,返回 0 成功(inc_dir 为所属 includes 根目录)""" if result is None or pool is None: return 1 if result.Count >= result.Capacity: @@ -127,6 +128,15 @@ def add_file_entry(result: ScanResult | t.CPtr, # 复制模块名 entry.ModuleName = mod_buf + # 复制所属 includes 根目录(多 includes 目录支持,Phase2 拼路径用) + if inc_dir is not None: + inc_len: t.CSizeT = string.strlen(inc_dir) + inc_buf: str = stdlib.malloc(inc_len + 1) + if inc_buf is None: + return 1 + string.strcpy(inc_buf, inc_dir) + entry.IncDir = inc_buf + result.Count += 1 return 0 @@ -193,8 +203,9 @@ def compute_file_sha1(pool: memhub.MemBuddy | t.CPtr, def scan_directory_recursive(pool: memhub.MemBuddy | t.CPtr, root_dir: str, rel_prefix: str, - result: ScanResult | t.CPtr) -> int: - """递归扫描目录,收集 .vp/.py 文件""" + result: ScanResult | t.CPtr, + inc_dir: str) -> int: + """递归扫描目录,收集 .vp/.py 文件(inc_dir 为所属 includes 根目录)""" if pool is None or root_dir is None or result is None: return 1 @@ -262,7 +273,7 @@ def scan_directory_recursive(pool: memhub.MemBuddy | t.CPtr, if is_dir != 0: # 递归扫描子目录 - scan_directory_recursive(pool, full_path, rel_path, result) + scan_directory_recursive(pool, full_path, rel_path, result, inc_dir) else: # 检查是否为 .vp 或 .py 文件(.vp 为 Viper 源,.py 向后兼容) is_py: int = 0 @@ -280,7 +291,7 @@ def scan_directory_recursive(pool: memhub.MemBuddy | t.CPtr, # 计算 SHA1 sha1: str = compute_file_sha1(pool, full_path) if sha1 is not None: - add_file_entry(result, pool, full_path, rel_path, sha1) + add_file_entry(result, pool, full_path, rel_path, sha1, inc_dir) fb: t.CChar | t.CPtr = VLogger.fmt_buf() if fb is not None: viperlib.snprintf(fb, 1024, "扫描: %s -> %s", rel_path, sha1) @@ -300,16 +311,36 @@ def scan_directory_recursive(pool: memhub.MemBuddy | t.CPtr, # 扫描指定的 includes 目录,返回所有 .vp/.py 文件的路径和 SHA1。 # ============================================================ def scan_includes(pool: memhub.MemBuddy | t.CPtr, - includes_dir: str) -> ScanResult | t.CPtr: - """扫描 includes 目录,返回所有 .vp/.py 文件的扫描结果""" - if pool is None or includes_dir is None: + includes_dirs: str) -> ScanResult | t.CPtr: + """扫描 includes 目录(支持空格分隔的多目录),返回所有 .vp/.py 文件的扫描结果""" + if pool is None or includes_dirs is None: return None result: ScanResult | t.CPtr = create_scan_result(pool) if result is None: return None - scan_directory_recursive(pool, includes_dir, None, result) + # 解析空格分隔的多 includes 目录(如 "../../includes ./includes"),逐目录递归扫描合并 + dirs_total: t.CSizeT = string.strlen(includes_dirs) + d_i: t.CSizeT = 0 + while d_i < dirs_total: + while d_i < dirs_total and includes_dirs[d_i] == ' ': + d_i += 1 + if d_i >= dirs_total: + break + d_start: t.CSizeT = d_i + while d_i < dirs_total and includes_dirs[d_i] != ' ': + d_i += 1 + d_len: t.CSizeT = d_i - d_start + if d_len == 0: + continue + dir_buf: bytes = stdlib.malloc(d_len + 1) + if dir_buf is None: + continue + string.strncpy(dir_buf, includes_dirs + d_start, d_len) + dir_buf[d_len] = '\0' + scan_directory_recursive(pool, dir_buf, None, result, dir_buf) + stdlib.free(dir_buf) return result diff --git a/App/lib/core/Phase2.vp b/App/lib/core/Phase2.vp index dd3e9ba..53575b8 100644 --- a/App/lib/core/Phase2.vp +++ b/App/lib/core/Phase2.vp @@ -281,7 +281,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, 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) + store_incdir_inc_pre: bytes | t.CPtr = StubMerger.GetSha1StoreIncdirArrPtr() deps_filled_pre: int = 0 PHASE_A_INC_MAX_PASSES: t.CInt = 3 # [修复] 记录预注册失败的 includes:多遍后仍失败 → 报错终止编译。 @@ -320,11 +320,16 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, inc_fail_flags[si_inc] = 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) + # 所属 includes 根目录(多目录逐条取自 IncDir 数组,空串回退 includes_dir) + incdir_pre: str = store_incdir_inc_pre + t.CSizeT(si_inc) * StubMerger.MAX_INC_DIR_LEN + if incdir_pre is None or incdir_pre[0] == '\0': + incdir_pre = includes_dir + incdir_len_pre: t.CSizeT = string.strlen(incdir_pre) + full_path_pre: bytes = stdlib.malloc(incdir_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) + viperlib.snprintf(full_path_pre, incdir_len_pre + rel_len_pre + 2, + "%s/%s", incdir_pre, 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) @@ -652,6 +657,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, 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() + store_incdir_arr_mi: bytes | t.CPtr = StubMerger.GetSha1StoreIncdirArrPtr() for si_mi in range(store_count_mi): # 获取当前条目的 SHA1 和 rel_path @@ -680,9 +686,12 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, continue # .obj 不存在,需要编译 - # 构造源文件路径: {includes_dir}/{rel_path} + # 构造源文件路径: {所属 includes 根目录}/{rel_path}(多目录逐条取 IncDir,空串回退 includes_dir) rel_path_len_mi: t.CSizeT = string.strlen(inc_rel_mi) - inc_dir_len_mi: t.CSizeT = string.strlen(includes_dir) + incdir_mi: str = store_incdir_arr_mi + t.CSizeT(si_mi) * StubMerger.MAX_INC_DIR_LEN + if incdir_mi is None or incdir_mi[0] == '\0': + incdir_mi = includes_dir + inc_dir_len_mi: t.CSizeT = string.strlen(incdir_mi) 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) @@ -690,7 +699,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, 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) + "%s/%s", incdir_mi, inc_rel_mi) # 检查是否为声明文件(只有 declare 没有实质 define) # 判断方法: text.ll 中若有混淆函数 define(含 @")则为实现文件 diff --git a/App/lib/core/StubMerger.vp b/App/lib/core/StubMerger.vp index 0eddc2e..fd27027 100644 --- a/App/lib/core/StubMerger.vp +++ b/App/lib/core/StubMerger.vp @@ -39,17 +39,18 @@ import lib.Projectrans.Config as Config _mbuddy: memhub.MemBuddy | t.CPtr # 最大 includes SHA1 数(用于过滤) -MAX_INCLUDES_SHA1: t.CDefine = 256 +MAX_INCLUDES_SHA1: t.CDefine = 1024 # stub 读取缓冲区大小 STUB_READ_BUF_SIZE: t.CDefine = 1048576 # 最大 includes 条目数 -MAX_INCLUDES: t.CDefine = 256 +MAX_INCLUDES: t.CDefine = 1024 # 全局 SHA1 映射存储器(内存中,不依赖 _sha1_map.txt 文件) # Phase1 扫描完成后填充,供所有函数使用 _sha1_store_arr: bytes # SHA1 数组(每个 17 字节,MAX_INCLUDES 个) _sha1_store_mod: bytes # 模块名数组(每个 64 字节,MAX_INCLUDES 个) _sha1_store_rel: bytes # 相对路径数组(每个 256 字节,MAX_INCLUDES 个) +_sha1_store_incdir: bytes # 所属 includes 根目录数组(每个 512 字节,MAX_INCLUDES 个,多 includes 支持) _sha1_store_count: int # 条目数 # includes 依赖内存存储(不依赖 deps.txt 过程文件) @@ -64,6 +65,8 @@ MAX_DEPS_STR_LEN: t.CDefine = 512 # rel_path 最大长度 MAX_REL_PATH_LEN: t.CDefine = 256 +# includes 根目录最大长度(绝对路径,多 includes 支持) +MAX_INC_DIR_LEN: t.CDefine = 512 # ============================================================ @@ -345,7 +348,7 @@ def PopulateSha1MapStore(scan_result: IncludesScanner.ScanResult | t.CPtr) -> in 模块名通过 _PathToModuleName(includes/rel_path) 计算。 内存用 stdlib.malloc 分配(全局存储器,不随 mbuddy 释放)。 """ - global _sha1_store_arr, _sha1_store_mod, _sha1_store_rel, _sha1_store_count + global _sha1_store_arr, _sha1_store_mod, _sha1_store_rel, _sha1_store_incdir, _sha1_store_count if scan_result is None: return -1 # 释放旧存储器 @@ -355,14 +358,18 @@ def PopulateSha1MapStore(scan_result: IncludesScanner.ScanResult | t.CPtr) -> in stdlib.free(_sha1_store_mod) if _sha1_store_rel is not None: stdlib.free(_sha1_store_rel) + if _sha1_store_incdir is not None: + stdlib.free(_sha1_store_incdir) _sha1_store_arr = stdlib.malloc(MAX_INCLUDES * 17) _sha1_store_mod = stdlib.malloc(MAX_INCLUDES * 64) _sha1_store_rel = stdlib.malloc(MAX_INCLUDES * MAX_REL_PATH_LEN) - if _sha1_store_arr is None or _sha1_store_mod is None or _sha1_store_rel is None: + _sha1_store_incdir = stdlib.malloc(MAX_INCLUDES * MAX_INC_DIR_LEN) + if _sha1_store_arr is None or _sha1_store_mod is None or _sha1_store_rel is None or _sha1_store_incdir is None: return -1 string.memset(_sha1_store_arr, 0, MAX_INCLUDES * 17) string.memset(_sha1_store_mod, 0, MAX_INCLUDES * 64) string.memset(_sha1_store_rel, 0, MAX_INCLUDES * MAX_REL_PATH_LEN) + string.memset(_sha1_store_incdir, 0, MAX_INCLUDES * MAX_INC_DIR_LEN) _sha1_store_count = 0 entry_size_p: t.CSizeT = IncludesScanner.FileEntry.__sizeof__() for i in range(scan_result.Count): @@ -398,6 +405,15 @@ def PopulateSha1MapStore(scan_result: IncludesScanner.ScanResult | t.CPtr) -> in else: string.strncpy(_sha1_store_rel + idx3, ent.RelPath, MAX_REL_PATH_LEN - 1) _sha1_store_rel[idx3 + MAX_REL_PATH_LEN - 1] = '\0' + # 保存所属 includes 根目录(Phase2 预注册/按需编译拼路径用) + idx_inc: t.CSizeT = t.CSizeT(_sha1_store_count) * MAX_INC_DIR_LEN + if ent.IncDir is not None: + icd_len: t.CSizeT = string.strlen(ent.IncDir) + if icd_len < MAX_INC_DIR_LEN: + string.strcpy(_sha1_store_incdir + idx_inc, ent.IncDir) + else: + string.strncpy(_sha1_store_incdir + idx_inc, ent.IncDir, MAX_INC_DIR_LEN - 1) + _sha1_store_incdir[idx_inc + MAX_INC_DIR_LEN - 1] = '\0' _sha1_store_count += 1 return _sha1_store_count @@ -424,7 +440,7 @@ def PopulateSha1MapStore(scan_result: IncludesScanner.ScanResult | t.CPtr) -> in # ============================================================ def AppendToSha1MapStore(sha1: str, rel_path: str) -> int: """追加条目到全局 SHA1 映射存储器(Phase2 用)""" - global _sha1_store_arr, _sha1_store_mod, _sha1_store_rel, _sha1_store_count + global _sha1_store_arr, _sha1_store_mod, _sha1_store_rel, _sha1_store_incdir, _sha1_store_count if sha1 is None or rel_path is None: return 1 # 存储器未初始化:自动初始化(Phase2 可能先于 Phase1 调用) @@ -432,11 +448,13 @@ def AppendToSha1MapStore(sha1: str, rel_path: str) -> int: _sha1_store_arr = stdlib.malloc(MAX_INCLUDES * 17) _sha1_store_mod = stdlib.malloc(MAX_INCLUDES * 64) _sha1_store_rel = stdlib.malloc(MAX_INCLUDES * MAX_REL_PATH_LEN) - if _sha1_store_arr is None or _sha1_store_mod is None or _sha1_store_rel is None: + _sha1_store_incdir = stdlib.malloc(MAX_INCLUDES * MAX_INC_DIR_LEN) + if _sha1_store_arr is None or _sha1_store_mod is None or _sha1_store_rel is None or _sha1_store_incdir is None: return 1 string.memset(_sha1_store_arr, 0, MAX_INCLUDES * 17) string.memset(_sha1_store_mod, 0, MAX_INCLUDES * 64) string.memset(_sha1_store_rel, 0, MAX_INCLUDES * MAX_REL_PATH_LEN) + string.memset(_sha1_store_incdir, 0, MAX_INCLUDES * MAX_INC_DIR_LEN) _sha1_store_count = 0 if _sha1_store_count >= MAX_INCLUDES: return 1 @@ -474,6 +492,9 @@ def AppendToSha1MapStore(sha1: str, rel_path: str) -> int: else: string.strncpy(_sha1_store_rel + idx_r, rel_path, MAX_REL_PATH_LEN - 1) _sha1_store_rel[idx_r + MAX_REL_PATH_LEN - 1] = '\0' + # App 源文件不属于任何 includes 目录:IncDir 留空(Phase2 回退 includes_dir 参数) + idx_inc_a: t.CSizeT = t.CSizeT(_sha1_store_count) * MAX_INC_DIR_LEN + _sha1_store_incdir[idx_inc_a] = '\0' _sha1_store_count += 1 return 0 @@ -639,6 +660,11 @@ def GetSha1StoreRelArrPtr() -> bytes | t.CPtr: return _sha1_store_rel +def GetSha1StoreIncdirArrPtr() -> bytes | t.CPtr: + """返回所属 includes 根目录数组指针(每个条目 MAX_INC_DIR_LEN 字节,空串表示不属于任何 includes 目录)""" + return _sha1_store_incdir + + # ============================================================ # GetSha1StoreArr - 获取全局存储器数组指针(直接遍历用) # diff --git a/App/main.vp b/App/main.vp index 176c66a..9f7aad1 100644 --- a/App/main.vp +++ b/App/main.vp @@ -304,11 +304,11 @@ def main() -> int: # === Phase1: 扫描 includes(按需翻译)=== # 对 includes 目录中每个 .vp/.py 文件,若 stub 不存在则翻译并分离 stub - if do_phase1 != 0 and Config.IncludesDir is not None: + if do_phase1 != 0 and Config.IncludesDirs is not None: ph1_temp: str = Config.TempDir if ph1_temp is None: ph1_temp = "." - Phase1.RunPhase1(mb, Config.IncludesDir, ph1_temp, log) + Phase1.RunPhase1(mb, Config.IncludesDirs, ph1_temp, log) # 如果仅 Phase1(不执行 Phase2),直接退出 if do_phase2 == 0: if log is not None: @@ -333,7 +333,7 @@ def main() -> int: mf_ret: int = Phase2.RunMultiFileProject( mb, Config.SourceDir, mf_temp, mf_output, mf_cc, mf_cc_flags, mf_linker, mf_linker_flags, mf_linker_out, - mf_includes_bin, Config.IncludesDir, do_phase1, do_phase2, log, args) + mf_includes_bin, Config.IncludesDirs, do_phase1, do_phase2, log, args) argparse.release(args) if log is not None: log.success("TransPyV 完成") diff --git a/project.vpj b/project.vpj new file mode 100644 index 0000000..ce786c1 --- /dev/null +++ b/project.vpj @@ -0,0 +1,29 @@ +{ + "$schema": "https://raw.githubusercontent.com/TermiNexus/TransPyC/main/schemas/project-schema.json", + "name": "TransPyV", + "version": "1.0.0", + "source_dir": "./App", + "build_dir": "./.tpv_build", + "compiler": { + "cmd": "llc", + "flags": ["-filetype=obj", "-relocation-model=pic"] + }, + "linker": { + "cmd": "clang++", + "flags": ["-lmsvcrt", "-lucrt", "-lpthread", "-lmingwex", "-lkernel32", "-Wl,--allow-multiple-definition", "-Wl,--stack,0x1000000"], + "output": "TransPyV.exe" + }, + "includes": [ + "../includes" + ], + "target": { + "triple": "x86_64-pc-windows-gnu", + "datalayout": "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + }, + "options": { + "slice_level": 3, + "sha1_slice_level": 1, + "target": "llvm", + "strict_mode": true + } +} \ No newline at end of file