681 lines
35 KiB
Python
681 lines
35 KiB
Python
import t, c
|
||
from stdint import *
|
||
import stdio
|
||
import string
|
||
import stdlib
|
||
import memhub
|
||
import viperlib
|
||
import w32.fileio as fileio
|
||
import w32.win32file
|
||
import w32.win32base
|
||
import sys
|
||
import ast
|
||
import llvmlite
|
||
import subprocess
|
||
import argparse
|
||
import lib.core.VLogger as VLogger
|
||
import lib.core.Handles.HandlesTranslator as HandlesTranslator
|
||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||
import lib.core.Handles.HandlesImports as HandlesImports
|
||
import lib.core.BuildPipeline as BuildPipeline
|
||
import lib.core.StubMerger as StubMerger
|
||
import lib.Projectrans.Utils as Utils
|
||
import lib.Projectrans.Config as Config
|
||
|
||
# 全局 mbuddy 指针
|
||
_mbuddy: memhub.MemManager | t.CPtr
|
||
|
||
# 源代码缓冲区大小(1MB)
|
||
SRC_BUF_SIZE: t.CDefine = 1048576
|
||
|
||
# 最大源文件数(递归扫描子目录后文件数增多,增大到 64)
|
||
MAX_SRC_FILES: t.CDefine = 64
|
||
|
||
|
||
@t.NoVTable
|
||
class SrcFileEntry:
|
||
"""源文件条目"""
|
||
Path: str # 完整路径
|
||
Sha1: str # SHA1 前16字符
|
||
|
||
|
||
# ============================================================
|
||
# _ScanDirForPyFiles - 递归扫描目录,收集 .py 文件到 entries
|
||
#
|
||
# 替代旧的非递归 FindFirstFileA(dir/*.py) 扫描,支持子目录
|
||
# (如 App/lib/core/Handles/)。遇到目录递归扫描,遇到 .py
|
||
# 文件读取内容、计算 SHA1、加入 entries。
|
||
#
|
||
# Args:
|
||
# mb: 内存池
|
||
# dir_path: 当前扫描目录
|
||
# entries: SrcFileEntry 数组
|
||
# entry_size: 单个条目大小(SrcFileEntry.__sizeof__())
|
||
# file_count: 当前已收集的文件数
|
||
# max_files: 最大文件数
|
||
#
|
||
# Returns:
|
||
# 新的 file_count
|
||
# ============================================================
|
||
def _ScanDirForPyFiles(mb: memhub.MemBuddy | t.CPtr,
|
||
dir_path: str,
|
||
entries: SrcFileEntry | t.CPtr,
|
||
entry_size: t.CSizeT,
|
||
file_count: int, max_files: int) -> int:
|
||
"""递归扫描目录,收集 .py 文件到 entries,返回新的 file_count"""
|
||
if dir_path is None or entries is None:
|
||
return file_count
|
||
if file_count >= max_files:
|
||
return file_count
|
||
|
||
dir_len: t.CSizeT = string.strlen(dir_path)
|
||
pattern: bytes = stdlib.malloc(dir_len + 16)
|
||
if pattern is None:
|
||
return file_count
|
||
viperlib.snprintf(pattern, dir_len + 16, "%s/*", dir_path)
|
||
|
||
find_data_size: t.CSizeT = w32.win32file.WIN32_FIND_DATAA.__sizeof__()
|
||
find_data: w32.win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(find_data_size + 16)
|
||
if find_data is None:
|
||
stdlib.free(pattern)
|
||
return file_count
|
||
string.memset(find_data, 0, find_data_size + 16)
|
||
|
||
handle: w32.win32base.HANDLE = w32.win32file.FindFirstFileA(pattern, find_data)
|
||
if handle == w32.win32base.INVALID_HANDLE_VALUE:
|
||
stdlib.free(pattern)
|
||
stdlib.free(find_data)
|
||
return file_count
|
||
|
||
while True:
|
||
fname: str = find_data.cFileName
|
||
if fname is not None:
|
||
fname_len: t.CSizeT = string.strlen(fname)
|
||
if fname_len > 0:
|
||
# 跳过 . 和 ..
|
||
is_dot: int = 0
|
||
if fname_len == 1 and fname[0] == '.':
|
||
is_dot = 1
|
||
elif fname_len == 2 and fname[0] == '.' and fname[1] == '.':
|
||
is_dot = 1
|
||
if is_dot == 0:
|
||
# 检查是否是目录
|
||
attrs: ULONG = find_data.dwFileAttributes
|
||
is_dir: int = 0
|
||
if (attrs & w32.win32file.FILE_ATTRIBUTE_DIRECTORY) != 0:
|
||
is_dir = 1
|
||
if is_dir != 0:
|
||
# 递归扫描子目录
|
||
sub_dir: bytes = stdlib.malloc(dir_len + fname_len + 2)
|
||
if sub_dir is not None:
|
||
viperlib.snprintf(sub_dir, dir_len + fname_len + 2, "%s/%s", dir_path, fname)
|
||
file_count = _ScanDirForPyFiles(mb, sub_dir, entries, entry_size, file_count, max_files)
|
||
stdlib.free(sub_dir)
|
||
else:
|
||
# 检查是否是 .py 文件
|
||
if fname_len > 3:
|
||
if fname[fname_len - 3] == '.' and fname[fname_len - 2] == 'p' and fname[fname_len - 1] == 'y':
|
||
if file_count < max_files:
|
||
full_path: bytes = stdlib.malloc(dir_len + fname_len + 2)
|
||
if full_path is not None:
|
||
viperlib.snprintf(full_path, dir_len + fname_len + 2, "%s/%s", dir_path, fname)
|
||
sf: fileio.File | t.CPtr = fileio.File(full_path, fileio.MODE.R)
|
||
if not sf.closed:
|
||
sbuf: bytes = stdlib.malloc(SRC_BUF_SIZE)
|
||
if sbuf is not None:
|
||
br: LONG = sf.read_all(sbuf, SRC_BUF_SIZE)
|
||
sf.close()
|
||
if br > 0:
|
||
if br < SRC_BUF_SIZE:
|
||
sbuf[br] = 0
|
||
else:
|
||
sbuf[SRC_BUF_SIZE - 1] = 0
|
||
sha1_val: str = Utils.compute_sha1(mb, sbuf)
|
||
if sha1_val is not None:
|
||
ea: t.CUInt64T = t.CUInt64T(entries) + file_count * entry_size
|
||
ent: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea, t.CPtr))
|
||
if ent is not None:
|
||
ent.Path = full_path
|
||
ent.Sha1 = sha1_val
|
||
file_count += 1
|
||
stdio.printf("[project] %s (sha1=%s)\n", fname, sha1_val)
|
||
stdlib.free(sbuf)
|
||
# full_path 不释放:ent.Path 引用它
|
||
|
||
if w32.win32file.FindNextFileA(handle, find_data) == 0:
|
||
break
|
||
|
||
w32.win32file.FindClose(handle)
|
||
stdlib.free(pattern)
|
||
stdlib.free(find_data)
|
||
return file_count
|
||
|
||
|
||
def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
|
||
source_dir: str, temp_dir: str, output_dir: str,
|
||
cc_cmd: str, cc_flags: str,
|
||
linker_cmd: str, linker_flags: str, linker_output: str,
|
||
includes_binary_dir: str,
|
||
includes_dir: str,
|
||
do_phase1: int, do_phase2: int,
|
||
log: VLogger.Logger | t.CPtr,
|
||
args: argparse.ParsedArgs | t.CPtr) -> int:
|
||
"""多文件项目编译
|
||
|
||
Returns: 0 成功,非 0 失败
|
||
"""
|
||
if source_dir is None:
|
||
stdio.printf("[project] source_dir 为空\n")
|
||
return 1
|
||
|
||
stdio.printf("[project] 多文件项目编译: %s\n", source_dir)
|
||
|
||
# === 1. 递归扫描 source_dir 下的 .py 文件(包括子目录)===
|
||
entry_size: t.CSizeT = SrcFileEntry.__sizeof__()
|
||
entries: SrcFileEntry | t.CPtr = stdlib.malloc(MAX_SRC_FILES * entry_size)
|
||
if entries is None:
|
||
return 1
|
||
string.memset(entries, 0, MAX_SRC_FILES * entry_size)
|
||
|
||
file_count: int = _ScanDirForPyFiles(mb, source_dir, entries, entry_size, 0, MAX_SRC_FILES)
|
||
stdio.printf("[project] 共 %d 个源文件\n", file_count)
|
||
|
||
if file_count == 0:
|
||
stdlib.free(entries)
|
||
return 1
|
||
|
||
# 初始化 AST 表
|
||
ast._init_tables(mb)
|
||
|
||
# 确保 temp/output 目录存在
|
||
BuildPipeline.ensure_dir(temp_dir)
|
||
BuildPipeline.ensure_dir(output_dir)
|
||
|
||
# 追加 App 源文件 SHA1 到 _sha1_map.txt(供日志转存和符号查找使用)
|
||
# 格式: {sha1}:App/{filename}\n (与 includes 条目格式对应)
|
||
td_len_am: t.CSizeT = string.strlen(temp_dir)
|
||
map_path_am: bytes = stdlib.malloc(td_len_am + 32)
|
||
if map_path_am is not None:
|
||
viperlib.snprintf(map_path_am, td_len_am + 32, "%s/_sha1_map.txt", temp_dir)
|
||
mf: fileio.File | t.CPtr = fileio.File(map_path_am, fileio.MODE.A)
|
||
if not mf.closed:
|
||
line_am: bytes = stdlib.malloc(512)
|
||
if line_am is not None:
|
||
for i in range(file_count):
|
||
ea_am: t.CUInt64T = t.CUInt64T(entries) + i * entry_size
|
||
ent_am: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea_am, t.CPtr))
|
||
if ent_am is None or ent_am.Path is None or ent_am.Sha1 is None:
|
||
continue
|
||
# 从路径提取文件名(最后一个 / 或 \\ 之后的部分)
|
||
p_str: str = ent_am.Path
|
||
p_len: t.CSizeT = string.strlen(p_str)
|
||
f_start: t.CSizeT = 0
|
||
for j in range(p_len):
|
||
if p_str[j] == '/' or p_str[j] == '\\':
|
||
f_start = j + 1
|
||
viperlib.snprintf(line_am, 512, "%s:App/%s\n", ent_am.Sha1, p_str + f_start)
|
||
ll_am: t.CSizeT = string.strlen(line_am)
|
||
mf.write(line_am, ll_am)
|
||
stdlib.free(line_am)
|
||
mf.close()
|
||
stdio.printf("[project] 已追加 %d 个 App 文件到 _sha1_map.txt\n", file_count)
|
||
stdlib.free(map_path_am)
|
||
|
||
# 构建模块 SHA1 映射(供跨模块函数调用名混淆使用)
|
||
td_len_pb: t.CSizeT = string.strlen(temp_dir)
|
||
pb_sha1_arr: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
||
pb_mod_arr: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
|
||
pb_inc_count: int = 0
|
||
if pb_sha1_arr is not None and pb_mod_arr is not None:
|
||
pb_inc_count = StubMerger._BuildIncludesSha1Map(temp_dir, td_len_pb, pb_sha1_arr, pb_mod_arr)
|
||
# 追加用户源文件的 (模块名, SHA1) 到全局映射
|
||
# 使跨模块方法调用能通过 from_imports + _lookup_module_sha1 找到正确的 SHA1
|
||
if pb_sha1_arr is not None and pb_mod_arr is not None:
|
||
for i in range(file_count):
|
||
ea_us: t.CUInt64T = t.CUInt64T(entries) + i * entry_size
|
||
ent_us: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea_us, t.CPtr))
|
||
if ent_us is None or ent_us.Path is None or ent_us.Sha1 is None:
|
||
continue
|
||
if pb_inc_count >= StubMerger.MAX_INCLUDES:
|
||
break
|
||
# 从路径提取模块名(文件名去掉 .py 后缀)
|
||
path_str: str = ent_us.Path
|
||
path_len: t.CSizeT = string.strlen(path_str)
|
||
fname_start: t.CSizeT = 0
|
||
for j in range(path_len):
|
||
if path_str[j] == '/' or path_str[j] == '\\':
|
||
fname_start = j + 1
|
||
mod_len: t.CSizeT = path_len - fname_start
|
||
if mod_len < 4:
|
||
continue
|
||
mod_len -= 3
|
||
if mod_len >= 64:
|
||
mod_len = 63
|
||
# 写入模块名
|
||
mod_idx: t.CSizeT = t.CSizeT(pb_inc_count) * 64
|
||
for k in range(mod_len):
|
||
pb_mod_arr[mod_idx + k] = path_str[fname_start + k]
|
||
pb_mod_arr[mod_idx + mod_len] = '\0'
|
||
# 写入 SHA1
|
||
sha1_idx: t.CSizeT = t.CSizeT(pb_inc_count) * 17
|
||
string.strcpy(pb_sha1_arr + sha1_idx, ent_us.Sha1)
|
||
pb_inc_count += 1
|
||
HandlesExprCall.set_module_sha1_map(pb_sha1_arr, pb_mod_arr, pb_inc_count)
|
||
|
||
|
||
# === 2. Phase A: 为每个文件生成 stub + text ===
|
||
if do_phase1 != 0:
|
||
if log is not None:
|
||
log.banner("Phase A: 生成 stub + text")
|
||
|
||
PHASE_A_IR_SIZE: t.CSizeT = 262144
|
||
td_len_pa: t.CSizeT = string.strlen(temp_dir)
|
||
|
||
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))
|
||
if ent is None or ent.Path is None:
|
||
continue
|
||
|
||
# 计算源文件的包名(相对 source_dir 的目录部分)
|
||
src_dir_len_pa: t.CSizeT = string.strlen(source_dir)
|
||
pkg_pa: str = None
|
||
if string.strlen(ent.Path) > src_dir_len_pa + 1:
|
||
rel_path_pa: str = ent.Path + src_dir_len_pa + 1
|
||
pkg_pa = HandlesImports.compute_package_from_relpath(mb, rel_path_pa)
|
||
tr_a: HandlesTranslator.Translator | t.CPtr = BuildPipeline.TranslateFileGetTrans(mb, ent.Path, ent.Sha1, pkg_pa)
|
||
if tr_a is None:
|
||
continue
|
||
|
||
# dump stub IR (declarations only)
|
||
stub_buf_a: bytes = stdlib.malloc(PHASE_A_IR_SIZE)
|
||
if stub_buf_a is None:
|
||
continue
|
||
tr_a.dump_ir(stub_buf_a, PHASE_A_IR_SIZE, llvmlite.OUTPUT_STUB)
|
||
stub_len_a: t.CSizeT = string.strlen(stub_buf_a)
|
||
|
||
# save stub.ll
|
||
stub_path_a: bytes = stdlib.malloc(td_len_pa + 32)
|
||
if stub_path_a is not None:
|
||
viperlib.snprintf(stub_path_a, td_len_pa + 32, "%s/%s.stub.ll", temp_dir, ent.Sha1)
|
||
sf_a: fileio.File | t.CPtr = fileio.File(stub_path_a, fileio.MODE.W)
|
||
if not sf_a.closed:
|
||
sf_a.write(stub_buf_a, stub_len_a)
|
||
sf_a.close()
|
||
stdlib.free(stub_path_a)
|
||
stdlib.free(stub_buf_a)
|
||
|
||
# dump text IR (definitions only)
|
||
text_buf_a: bytes = stdlib.malloc(PHASE_A_IR_SIZE)
|
||
if text_buf_a is None:
|
||
continue
|
||
tr_a.dump_ir(text_buf_a, PHASE_A_IR_SIZE, llvmlite.OUTPUT_TEXT)
|
||
text_len_a: t.CSizeT = string.strlen(text_buf_a)
|
||
|
||
# save text.ll
|
||
text_path_a: bytes = stdlib.malloc(td_len_pa + 32)
|
||
if text_path_a is not None:
|
||
viperlib.snprintf(text_path_a, td_len_pa + 32, "%s/%s.text.ll", temp_dir, ent.Sha1)
|
||
tf_a: fileio.File | t.CPtr = fileio.File(text_path_a, fileio.MODE.W)
|
||
if not tf_a.closed:
|
||
tf_a.write(text_buf_a, text_len_a)
|
||
tf_a.close()
|
||
stdlib.free(text_path_a)
|
||
stdlib.free(text_buf_a)
|
||
|
||
# save dependencies (_imported_modules) for Phase B
|
||
deps_path_a: bytes = stdlib.malloc(td_len_pa + 32)
|
||
if deps_path_a is not None:
|
||
viperlib.snprintf(deps_path_a, td_len_pa + 32, "%s/%s.deps.txt", temp_dir, ent.Sha1)
|
||
df_a: fileio.File | t.CPtr = fileio.File(deps_path_a, fileio.MODE.W)
|
||
if not df_a.closed:
|
||
if tr_a._imported_modules is not None:
|
||
dl_a: t.CSizeT = string.strlen(tr_a._imported_modules)
|
||
df_a.write(tr_a._imported_modules, dl_a)
|
||
df_a.close()
|
||
stdlib.free(deps_path_a)
|
||
|
||
if do_phase2 == 0:
|
||
stdio.printf("[project] Phase A 完成(仅 stub 生成)\n")
|
||
stdlib.free(entries)
|
||
return 0
|
||
|
||
# === 3. Phase B: 编译每个文件为 .obj ===
|
||
if do_phase2 != 0:
|
||
if log is not None:
|
||
log.banner("Phase B: 编译 .obj")
|
||
|
||
# 收集 .obj 路径
|
||
OBJ_PATHS_SIZE: t.CSizeT = 8192
|
||
obj_paths: bytes = stdlib.malloc(OBJ_PATHS_SIZE)
|
||
if obj_paths is None:
|
||
return 1
|
||
obj_paths[0] = '\0'
|
||
obj_pos: t.CSizeT = 0
|
||
# main_obj_path: 存放定义了用户 main 的 .obj 路径(链接时放在最前面,
|
||
# 避免 --allow-multiple-definition 选择了其他模块的 wrapper main)
|
||
main_obj_path: bytes = stdlib.malloc(512)
|
||
if main_obj_path is None:
|
||
return 1
|
||
main_obj_path[0] = '\0'
|
||
compiled_count: int = 0
|
||
|
||
# 组合 IR 缓冲区大小(4MB,足够容纳本地 stub + 所有依赖 stub + 本地 text)
|
||
COMBINED_IR_SIZE: t.CSizeT = 4194304
|
||
|
||
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))
|
||
if ent is None or ent.Path is None or ent.Sha1 is None:
|
||
continue
|
||
|
||
stdio.printf("[Phase B] %s\n", ent.Path)
|
||
|
||
# 组合本地 stub + 所有依赖 stub + 本地 text → 完整 IR
|
||
stdio.printf("[Phase B] malloc %d bytes...\n", COMBINED_IR_SIZE)
|
||
combined_ir: bytes = stdlib.malloc(COMBINED_IR_SIZE)
|
||
if combined_ir is None:
|
||
stdio.printf("[Phase B] combined_ir 分配失败: %s\n", ent.Path)
|
||
continue
|
||
stdio.printf("[Phase B] malloc OK, calling BuildCombinedIR...\n")
|
||
combined_len: t.CSizeT = StubMerger.BuildCombinedIR(temp_dir, ent.Sha1, combined_ir, COMBINED_IR_SIZE)
|
||
if combined_len == 0:
|
||
stdio.printf("[Phase B] BuildCombinedIR 失败: %s\n", ent.Path)
|
||
stdlib.free(combined_ir)
|
||
continue
|
||
|
||
# 编译为 .obj
|
||
cret: int = BuildPipeline.compile_module_to_obj(
|
||
combined_ir, combined_len, temp_dir, output_dir, ent.Sha1,
|
||
cc_cmd, cc_flags)
|
||
stdlib.free(combined_ir)
|
||
if cret != 0:
|
||
stdio.printf("[FATAL][Phase B] llc 编译失败,终止编译: %s\n", ent.Path)
|
||
sys.exit(1)
|
||
|
||
compiled_count += 1
|
||
|
||
# 构造 .obj 路径,检测是否是 main 模块(test_main.py 或 main.py)
|
||
od_len: t.CSizeT = string.strlen(output_dir)
|
||
sha1_len: t.CSizeT = string.strlen(ent.Sha1)
|
||
need: t.CSizeT = od_len + 1 + sha1_len + 6
|
||
is_main_mod: int = 0
|
||
if string.strstr(ent.Path, "test_main.py") is not None:
|
||
is_main_mod = 1
|
||
elif string.strstr(ent.Path, "main.py") is not None:
|
||
is_main_mod = 1
|
||
|
||
if is_main_mod != 0:
|
||
viperlib.snprintf(main_obj_path, 512, "%s/%s.obj", output_dir, ent.Sha1)
|
||
else:
|
||
if obj_pos + need < OBJ_PATHS_SIZE:
|
||
if obj_pos > 0:
|
||
obj_paths[obj_pos] = ' '
|
||
obj_pos += 1
|
||
viperlib.snprintf(obj_paths + obj_pos, need, "%s/%s.obj", output_dir, ent.Sha1)
|
||
obj_pos += od_len + 1 + sha1_len + 4
|
||
obj_paths[obj_pos] = '\0'
|
||
else:
|
||
stdio.printf("[Phase B] 警告: .obj 路径缓冲区不足\n")
|
||
|
||
stdio.printf("[Phase B] 编译完成: %d/%d\n", compiled_count, file_count)
|
||
|
||
if compiled_count == 0:
|
||
stdio.printf("[Phase B] 无成功编译的文件\n")
|
||
stdlib.free(entries)
|
||
return 1
|
||
|
||
# === 3.5 编译缺失的 includes 文件 ===
|
||
# includes.binary 可能缺少某些 includes .obj(如 testcheck.py),
|
||
# 这些文件被用户项目导入但未被 TransPyV 自身依赖,Projectrans.py 未编译它们。
|
||
# 检测并编译缺失的 includes 文件到 output_dir,加入链接命令。
|
||
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)
|
||
map_path_mi: bytes = stdlib.malloc(td_len_mi + 32)
|
||
if map_path_mi is not None:
|
||
viperlib.snprintf(map_path_mi, td_len_mi + 32, "%s/_sha1_map.txt", temp_dir)
|
||
mapf_mi: fileio.File | t.CPtr = fileio.File(map_path_mi, fileio.MODE.R)
|
||
if not mapf_mi.closed:
|
||
map_buf_mi: bytes = stdlib.malloc(65536)
|
||
if map_buf_mi is not None:
|
||
map_br_mi: t.CInt64T = mapf_mi.read_all(map_buf_mi, 65536)
|
||
mapf_mi.close()
|
||
if map_br_mi > 0:
|
||
if map_br_mi < 65536:
|
||
map_buf_mi[map_br_mi] = '\0'
|
||
else:
|
||
map_buf_mi[65535] = '\0'
|
||
map_len_mi: t.CSizeT = map_br_mi
|
||
mpos_mi: t.CSizeT = 0
|
||
while mpos_mi < map_len_mi:
|
||
ml_start_mi: t.CSizeT = mpos_mi
|
||
while mpos_mi < map_len_mi:
|
||
if map_buf_mi[mpos_mi] == '\n':
|
||
break
|
||
mpos_mi += 1
|
||
ml_len_mi: t.CSizeT = mpos_mi - ml_start_mi
|
||
mpos_mi += 1
|
||
|
||
# 最小长度: 16(sha1)+1(:)+9(includes/)+1+1+1 = 29
|
||
if ml_len_mi < 26:
|
||
continue
|
||
if map_buf_mi[ml_start_mi + 16] != ':':
|
||
continue
|
||
rp_start_mi: t.CSizeT = ml_start_mi + 17
|
||
if string.strncmp(map_buf_mi + rp_start_mi, "includes/", 9) != 0:
|
||
continue
|
||
|
||
# 提取 SHA1
|
||
inc_sha1_mi: str = stdlib.malloc(17)
|
||
if inc_sha1_mi is None:
|
||
continue
|
||
string.strncpy(inc_sha1_mi, map_buf_mi + ml_start_mi, 16)
|
||
inc_sha1_mi[16] = '\0'
|
||
|
||
# 检查 .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_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:
|
||
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:
|
||
continue
|
||
|
||
# .obj 不存在,需要编译
|
||
# 构造源文件路径: {includes_dir}/{rel_path_without_includes_prefix}
|
||
rel_path_len_mi: t.CSizeT = ml_len_mi - 26
|
||
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:
|
||
continue
|
||
viperlib.snprintf(src_fp_mi, inc_dir_len_mi + 1 + rel_path_len_mi + 1,
|
||
"%s/%s", includes_dir, map_buf_mi + rp_start_mi + 9)
|
||
|
||
# 检查是否为声明文件(只有 declare 没有实质 define)
|
||
# 判断方法: text.ll 中若有混淆函数 define(含 @\")则为实现文件
|
||
is_decl_mi: int = -1
|
||
tpath_mi: bytes = stdlib.malloc(td_len_mi + 32)
|
||
if tpath_mi is not None:
|
||
viperlib.snprintf(tpath_mi, td_len_mi + 32, "%s/%s.text.ll", temp_dir, inc_sha1_mi)
|
||
tf_mi: fileio.File | t.CPtr = fileio.File(tpath_mi, fileio.MODE.R)
|
||
if not tf_mi.closed:
|
||
is_decl_mi = 1
|
||
tbuf_mi: bytes = stdlib.malloc(StubMerger.STUB_READ_BUF_SIZE)
|
||
if tbuf_mi is not None:
|
||
tbr_mi: t.CInt64T = tf_mi.read_all(tbuf_mi, StubMerger.STUB_READ_BUF_SIZE)
|
||
if tbr_mi > 0:
|
||
if tbr_mi < StubMerger.STUB_READ_BUF_SIZE:
|
||
tbuf_mi[tbr_mi] = '\0'
|
||
else:
|
||
tbuf_mi[StubMerger.STUB_READ_BUF_SIZE - 1] = '\0'
|
||
# 逐行扫描: 找 define 行中含 @\" 的(混淆函数名)
|
||
tpos_mi: t.CSizeT = 0
|
||
while tpos_mi < tbr_mi:
|
||
tls_mi: t.CSizeT = tpos_mi
|
||
while tpos_mi < tbr_mi:
|
||
if tbuf_mi[tpos_mi] == '\n':
|
||
break
|
||
tpos_mi += 1
|
||
tll_mi: t.CSizeT = tpos_mi - tls_mi
|
||
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
|
||
stdlib.free(tbuf_mi)
|
||
tf_mi.close()
|
||
stdlib.free(tpath_mi)
|
||
if is_decl_mi == 1:
|
||
stdio.printf("[Phase B+] 跳过(声明文件): %s (sha1=%s)\n", src_fp_mi, inc_sha1_mi)
|
||
continue
|
||
|
||
stdio.printf("[Phase B+] 编译缺失 includes: %s (sha1=%s)\n", src_fp_mi, inc_sha1_mi)
|
||
|
||
# 尝试 BuildCombinedIR(stub/text 应已由 Phase1 生成)
|
||
inc_combined_mi: bytes = stdlib.malloc(COMBINED_IR_SIZE)
|
||
inc_combined_len_mi: t.CSizeT = 0
|
||
if inc_combined_mi is not None:
|
||
inc_combined_len_mi = StubMerger.BuildCombinedIR(temp_dir, inc_sha1_mi, inc_combined_mi, COMBINED_IR_SIZE)
|
||
|
||
# 如果 stub/text 不存在,翻译源文件并保存 stub + text,然后重试
|
||
if inc_combined_len_mi == 0 and inc_combined_mi is not None:
|
||
stdio.printf("[Phase B+] stub/text 不存在,翻译: %s\n", src_fp_mi)
|
||
# 计算 includes 文件的包名(相对 includes_dir 的目录部分)
|
||
inc_pkg_mi: str = None
|
||
if string.strlen(src_fp_mi) > inc_dir_len_mi + 1:
|
||
inc_rel_mi: str = src_fp_mi + inc_dir_len_mi + 1
|
||
inc_pkg_mi = HandlesImports.compute_package_from_relpath(mb, inc_rel_mi)
|
||
tr_mi: HandlesTranslator.Translator | t.CPtr = BuildPipeline.TranslateFileGetTrans(mb, src_fp_mi, inc_sha1_mi, inc_pkg_mi)
|
||
if tr_mi is not None:
|
||
PBP_IR_SIZE: t.CSizeT = 262144
|
||
# 保存 stub.ll
|
||
inc_stub_buf: bytes = stdlib.malloc(PBP_IR_SIZE)
|
||
if inc_stub_buf is not None:
|
||
tr_mi.dump_ir(inc_stub_buf, PBP_IR_SIZE, llvmlite.OUTPUT_STUB)
|
||
inc_stub_len: t.CSizeT = string.strlen(inc_stub_buf)
|
||
inc_stub_path: bytes = stdlib.malloc(td_len_mi + 32)
|
||
if inc_stub_path is not None:
|
||
viperlib.snprintf(inc_stub_path, td_len_mi + 32, "%s/%s.stub.ll", temp_dir, inc_sha1_mi)
|
||
isf: fileio.File | t.CPtr = fileio.File(inc_stub_path, fileio.MODE.W)
|
||
if not isf.closed:
|
||
isf.write(inc_stub_buf, inc_stub_len)
|
||
isf.close()
|
||
stdlib.free(inc_stub_path)
|
||
stdlib.free(inc_stub_buf)
|
||
# 保存 text.ll
|
||
inc_text_buf: bytes = stdlib.malloc(PBP_IR_SIZE)
|
||
if inc_text_buf is not None:
|
||
tr_mi.dump_ir(inc_text_buf, PBP_IR_SIZE, llvmlite.OUTPUT_TEXT)
|
||
inc_text_len: t.CSizeT = string.strlen(inc_text_buf)
|
||
inc_text_path: bytes = stdlib.malloc(td_len_mi + 32)
|
||
if inc_text_path is not None:
|
||
viperlib.snprintf(inc_text_path, td_len_mi + 32, "%s/%s.text.ll", temp_dir, inc_sha1_mi)
|
||
itf: fileio.File | t.CPtr = fileio.File(inc_text_path, fileio.MODE.W)
|
||
if not itf.closed:
|
||
itf.write(inc_text_buf, inc_text_len)
|
||
itf.close()
|
||
stdlib.free(inc_text_path)
|
||
stdlib.free(inc_text_buf)
|
||
# 重试 BuildCombinedIR
|
||
inc_combined_len_mi = StubMerger.BuildCombinedIR(temp_dir, inc_sha1_mi, inc_combined_mi, COMBINED_IR_SIZE)
|
||
|
||
if inc_combined_len_mi == 0:
|
||
stdio.printf("[Phase B+] BuildCombinedIR 失败: %s\n", src_fp_mi)
|
||
if inc_combined_mi is not None:
|
||
stdlib.free(inc_combined_mi)
|
||
continue
|
||
|
||
# 编译为 .obj
|
||
inc_cret_mi: int = BuildPipeline.compile_module_to_obj(
|
||
inc_combined_mi, inc_combined_len_mi, temp_dir, output_dir, inc_sha1_mi,
|
||
cc_cmd, cc_flags)
|
||
stdlib.free(inc_combined_mi)
|
||
if inc_cret_mi != 0:
|
||
stdio.printf("[FATAL][Phase B+] llc 编译失败,终止编译: %s\n", src_fp_mi)
|
||
sys.exit(1)
|
||
|
||
inc_compiled += 1
|
||
|
||
# 添加到 obj_paths
|
||
od_len_mi: t.CSizeT = string.strlen(output_dir)
|
||
sha1_len_mi: t.CSizeT = 16
|
||
need_mi: t.CSizeT = od_len_mi + 1 + sha1_len_mi + 6
|
||
if obj_pos + need_mi < OBJ_PATHS_SIZE:
|
||
if obj_pos > 0:
|
||
obj_paths[obj_pos] = ' '
|
||
obj_pos += 1
|
||
viperlib.snprintf(obj_paths + obj_pos, need_mi, "%s/%s.obj", output_dir, inc_sha1_mi)
|
||
obj_pos += od_len_mi + 1 + sha1_len_mi + 4
|
||
obj_paths[obj_pos] = '\0'
|
||
|
||
stdio.printf("[Phase B+] 编译缺失 includes: %d 个\n", inc_compiled)
|
||
|
||
# === 4. Phase C: 链接所有 .obj → .exe ===
|
||
if log is not None:
|
||
log.banner("Phase C: 链接")
|
||
|
||
od_len2: t.CSizeT = string.strlen(output_dir)
|
||
lo_len: t.CSizeT = string.strlen(linker_output)
|
||
exe_path: bytes = stdlib.malloc(od_len2 + lo_len + 2)
|
||
if exe_path is not None:
|
||
viperlib.snprintf(exe_path, od_len2 + lo_len + 2, "%s/%s", output_dir, linker_output)
|
||
else:
|
||
exe_path = linker_output
|
||
|
||
# 构造最终 .obj 路径列表:main_obj_path 在前,其他 .obj 在后
|
||
final_obj_paths: bytes = stdlib.malloc(OBJ_PATHS_SIZE + 512)
|
||
if final_obj_paths is None:
|
||
stdlib.free(entries)
|
||
return 1
|
||
final_obj_paths[0] = '\0'
|
||
fop_pos: t.CSizeT = 0
|
||
# main_obj_path 放在最前面(确保 --allow-multiple-definition 选择用户 main)
|
||
if main_obj_path[0] != '\0':
|
||
mlen: t.CSizeT = string.strlen(main_obj_path)
|
||
string.strcpy(final_obj_paths, main_obj_path)
|
||
fop_pos = mlen
|
||
if obj_paths[0] != '\0':
|
||
final_obj_paths[fop_pos] = ' '
|
||
fop_pos += 1
|
||
# 追加其他 .obj
|
||
if obj_paths[0] != '\0':
|
||
string.strcpy(final_obj_paths + fop_pos, obj_paths)
|
||
fop_pos += string.strlen(obj_paths)
|
||
final_obj_paths[fop_pos] = '\0'
|
||
|
||
obj_paths_len: t.CSizeT = fop_pos
|
||
lret: int = BuildPipeline.link_objs_to_exe(
|
||
final_obj_paths, obj_paths_len,
|
||
linker_cmd, linker_flags, exe_path,
|
||
includes_binary_dir)
|
||
|
||
if lret == 0:
|
||
stdio.printf("输出: %s\n", exe_path)
|
||
if args.get_bool("run"):
|
||
stdio.printf("[run] 执行: %s\n", exe_path)
|
||
rp: subprocess.CompletedProcess | t.CPtr = subprocess.run(exe_path, False, False)
|
||
if rp is not None:
|
||
stdio.printf("[run] 退出码: %d\n", rp.returncode)
|
||
else:
|
||
stdio.printf("[Phase C] 链接失败\n")
|
||
stdlib.free(entries)
|
||
return 1
|
||
|
||
stdlib.free(entries)
|
||
return 0
|