Files
ViperC/App/lib/Projectrans/Config.vp
T
2026-09-21 16:17:19 +08:00

729 lines
30 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import t, c
from stdint import *
import memhub
import string
import stdio
import stdlib
import viperlib
import lib.core.VLogger as VLogger
from json.__parser import parse as json_parse
from json import JsonValue, JSON_OBJECT, JSON_INT
import w32.fileio as fileio
# ============================================================
# Config - 工程配置
# ============================================================
# 全局 mbuddy 指针
_mbuddy: rawptr[memhub.MemManager]
# 配置值(从 project.vpj 加载);默认 None/0:未加载时字段为 null,调用方须 is not None 检查兜底(否则垃圾指针 → 崩溃)
SourceDir: str = None
BuildDir: str = None
TempDir: str = None # = {BuildDir}/temp(自动计算)
OutputDir: str = None # = {BuildDir}/output(自动计算)
ProjectName: str = None
ProjectVersion: str = None
CompilerCmd: str = None
CompilerFlags: str = None # 编译器参数(空格分隔,从 compiler.flags 数组拼接)
LinkerCmd: str = None
LinkerFlags: str = None # 链接器参数(空格分隔,从 linker.flags 数组拼接)
LinkerOutput: str = None
LinkerStaticLibs: str = None # 显式静态链接文件(空格分隔绝对路径,从 linker.static_libs 数组)
LinkerDynamicLibs: str = None # 显式动态链接文件(空格分隔绝对路径,从 linker.dynamic_libs 数组,.dll/.so)
TargetTriple: str = None
TargetDataLayout: str = None
ProjectDir: str = None # project.vpj 所在目录(resolve_paths 设置)
Sha1SliceLevel: int = 0 # SHA1 切片层数(目录分散等级)
StrictMode: int = 0
IncludesDir: str = None # includes 目录路径(从 includes 数组首元素,兼容单目录场景)
IncludesDirs: str = None # 全部 includes 目录(空格分隔,多 includes 目录支持)
# includes 预编译缓存目录后缀(asan 模式用 .binary_asan 隔离 asan 插桩 obj,避免与非 asan obj 混链)
_inc_bin_suffix: str = ".binary"
def Load_project_config(path: str) -> int:
"""加载工程配置文件(project.vpj)
Args:
path: 配置文件路径
Returns:
0 表示成功,非 0 表示失败
"""
global SourceDir, BuildDir, TempDir, OutputDir, ProjectName, ProjectVersion
global CompilerCmd, CompilerFlags, LinkerCmd, LinkerFlags, LinkerOutput, LinkerStaticLibs, LinkerDynamicLibs
global TargetTriple, TargetDataLayout, Sha1SliceLevel, StrictMode
global IncludesDir, IncludesDirs
if path is None: return 1
if _mbuddy is None: return 1
# 打开文件
f: rawptr[fileio.File] = fileio.File(path, fileio.MODE.R)
if f is None: return 1
if f.closed: return 1
# 分配读取缓冲区
CFG_BUF_SIZE: sizet = 8192
buf: bytes = _mbuddy.alloc(CFG_BUF_SIZE)
if buf is None:
f.close()
return 1
bytes_read: int64 = f.read_all(buf, CFG_BUF_SIZE)
f.close()
if bytes_read <= 0: return 1
# 解析 JSON
root: JsonValue | t.CPtr = json_parse(_mbuddy, buf)
if root is None: return 1
if not root.is_object(): return 1
# 读取顶层字段(使用显式 __getitem__ 调用,兼容旧编译器二进制)
sd_val: JsonValue | t.CPtr = root["source_dir"]
SourceDir = sd_val.as_string() if sd_val is not None else None
# build_dir: 统一构建目录(默认 ./.tpv_build),temp 和 output 作为其子目录
bd_val: JsonValue | t.CPtr = root["build_dir"]
if bd_val is not None and bd_val.is_string():
BuildDir = bd_val.as_string()
else:
BuildDir = "./.tpv_build"
name_val: JsonValue | t.CPtr = root["name"]
ProjectName = name_val.as_string() if name_val is not None else None
ver_val: JsonValue | t.CPtr = root["version"]
ProjectVersion = ver_val.as_string() if ver_val is not None else None
# 读取 compiler 子对象
compiler: JsonValue | t.CPtr = root["compiler"]
if compiler is not None and compiler.is_object():
cc_cmd_val: JsonValue | t.CPtr = compiler["cmd"]
CompilerCmd = cc_cmd_val.as_string() if cc_cmd_val is not None else None
# 读取 compiler.flags 数组,拼接成空格分隔的字符串
cc_flags_val: JsonValue | t.CPtr = compiler["flags"]
CompilerFlags = _join_flags_array(cc_flags_val)
# 读取 linker 子对象
linker: JsonValue | t.CPtr = root["linker"]
if linker is not None and linker.is_object():
ld_cmd_val: JsonValue | t.CPtr = linker["cmd"]
LinkerCmd = ld_cmd_val.as_string() if ld_cmd_val is not None else None
ld_out_val: JsonValue | t.CPtr = linker["output"]
LinkerOutput = ld_out_val.as_string() if ld_out_val is not None else None
# 读取 linker.flags 数组,拼接成空格分隔的字符串
ld_flags_val: JsonValue | t.CPtr = linker["flags"]
LinkerFlags = _join_flags_array(ld_flags_val)
# 读取 linker.static_libs 数组:显式静态链接文件(.a/.obj),
# 先拼接原样字符串,resolve_paths 中再基于 project_dir 解析绝对路径
sl_val: JsonValue | t.CPtr = linker["static_libs"]
LinkerStaticLibs = _join_flags_array(sl_val)
# 读取 linker.dynamic_libs 数组:显式动态链接文件(.dll/.so),
# 同样先拼接原样字符串,resolve_paths 中基于 project_dir 解析绝对路径
dl_val: JsonValue | t.CPtr = linker["dynamic_libs"]
LinkerDynamicLibs = _join_flags_array(dl_val)
# 读取 target 子对象
target: JsonValue | t.CPtr = root["target"]
if target is not None and target.is_object():
tgt_triple_val: JsonValue | t.CPtr = target["triple"]
TargetTriple = tgt_triple_val.as_string() if tgt_triple_val is not None else None
tgt_dl_val: JsonValue | t.CPtr = target["datalayout"]
TargetDataLayout = tgt_dl_val.as_string() if tgt_dl_val is not None else None
# 读取 options 子对象
# Sha1SliceLevel: SHA1 切片层数(默认 1,每层取 SHA1 前 2 字符作为子目录:0=不切片, 1=b7/, 2=b7/90/)
options: JsonValue | t.CPtr = root["options"]
Sha1SliceLevel = 1
StrictMode = 1
if options is not None and options.is_object():
sl: JsonValue | t.CPtr = options["sha1_slice_level"]
if sl is not None and sl.is_int():
Sha1SliceLevel = sl.as_int()
sm: JsonValue | t.CPtr = options["strict_mode"]
if sm is not None and sm.is_bool():
StrictMode = 1 if sm.as_bool() else 0
# 读取 includes 数组(支持多目录:首元素存 IncludesDir,全部拼接存 IncludesDirs)
includes_val: JsonValue | t.CPtr = root["includes"]
if includes_val is not None and includes_val.is_array():
arr_len: sizet = len(includes_val)
inc_buf: bytes = stdlib.malloc(2048)
if inc_buf is not None:
inc_buf[0] = '\0'
inc_pos: sizet = 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: sizet = 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
def _join_flags_array(flags_val: JsonValue | t.CPtr) -> str:
"""将 JSON 字符串数组拼接成空格分隔的字符串
用于 linker.flags 和 compiler.flags 数组。
用 while + 索引遍历,避免 list 迭代器 bug。
Args:
flags_val: JsonValue,应为字符串数组
Returns:
空格分隔的字符串,或 None 如果 flags_val 为 None 或非数组或空数组
"""
if flags_val is None: return None
if not flags_val.is_array(): return None
arr_len: sizet = len(flags_val)
if arr_len == 0:
return None
# 第一遍:计算总长度(所有 flag 长度 + 空格分隔符)
total_len: sizet = 0
for i in range(arr_len):
item: JsonValue | t.CPtr = flags_val.get_item(i)
if item is not None and item.is_string():
s: str = item.as_string()
if s is not None:
total_len += string.strlen(s) + 1 # +1 for space
if total_len == 0:
return None
# 分配缓冲区(total_len 已含每个 flag 后的空格,最后一个是 '\0')
buf: str = _mbuddy.alloc(total_len + 1)
if buf is None:
return None
# 第二遍:拼接(flag 之间用空格分隔)
pos: sizet = 0
for i in range(arr_len):
item = flags_val.get_item(i)
if item is not None and item.is_string():
s = item.as_string()
if s is not None:
slen: sizet = string.strlen(s)
if pos > 0:
buf[pos] = ' '
pos += 1
string.strcpy(buf + pos, s)
pos += slen
buf[pos] = '\0'
return buf
def _join_path(rel: str, project_dir: str) -> str:
"""将相对路径与 project_dir 拼接为绝对路径
- ./App + Test → Test/App
- ./temp + Test → Test/temp
- ../includes + Test → Test/../includes(OS 解析为 includes)
- 绝对路径保持不变
- None 返回 None
拼接结果经 _normalize_path 规范化:./Test/../includes → ./includes,
消除 .. 段,保证路径显示干净且相对寻址正确。
含 %VAR% 的路径(如 repl.vpj 的 build_dir = %temp%/viperepl/.tpv_build)
先经 fileio 路径层展开为实路径:TempDir/OutputDir 等由 BuildDir 派生,
且后续消费者不止文件打开(ensure_dir 直接调 CreateDirectoryA、
collect_obj_files 调 FindFirstFileA、命令串走 CreateProcessA),
故展开必须发生在**配置路径设置层**而不是逐个调用点。
"""
if rel is None:
return None
# 环境变量展开:仅在含 '%' 时生效(fileio.ExpandEnvironmentPath 自带
# "失败/无 '%' 返回 None" 契约),展开失败时保持原串不变
ep: str = fileio.ExpandEnvironmentPath(rel)
if ep is not None:
rel = ep
# 绝对路径(以 / 或 \ 开头)保持不变
if rel[0] == '/' or rel[0] == '\\':
return rel
# 路径已含 project_dir 前缀时不再拼接,避免双份路径(./TransPyV/TransPyV/.tpv_build2);边界取到目录分隔符或串尾
if project_dir is not None:
pdl: sizet = string.strlen(project_dir)
if pdl > 0 and string.strncmp(rel, project_dir, pdl) == 0:
if rel[pdl] == '/' or rel[pdl] == '\\' or rel[pdl] == '\0':
return rel
rel_len: sizet = string.strlen(rel)
dir_len: sizet = string.strlen(project_dir)
# 处理 ../ 或 ..\\ 前缀:拼接 project_dir/../...
if rel_len >= 3 and rel[0] == '.' and rel[1] == '.':
if rel[2] == '/' or rel[2] == '\\':
if dir_len == 0:
return rel
total_len: sizet = dir_len + 1 + rel_len + 1
buf: str = _mbuddy.alloc(total_len)
if buf is None:
return rel
viperlib.snprintf(buf, total_len, "%s/%s", project_dir, rel)
return _normalize_path(buf)
# 处理 ./ 或 .\\ 前缀:拼接 project_dir + rel[2:]
rel_off: sizet = 0
if rel_len >= 2 and rel[0] == '.':
if rel[1] == '/' or rel[1] == '\\':
rel_off = 2
else:
return rel # 不是 ./ 开头,保持不变
else:
return rel # 不是 . 开头,保持不变
if dir_len == 0:
# project_dir 为空(当前目录),去掉 ./ 前缀即可
return rel + rel_off
# 拼接: project_dir + "/" + rel[rel_off:]
total_len = dir_len + 1 + (rel_len - rel_off) + 1
buf = _mbuddy.alloc(total_len)
if buf is None:
return rel # 分配失败,返回原路径
viperlib.snprintf(buf, total_len, "%s/%s", project_dir, rel + rel_off)
return _normalize_path(buf)
def _normalize_path(path: str) -> str:
"""规范化路径:解析 . 和 .. 段,合并多余分隔符
例: ./test/BuddyTest/../../includes → ./includes
./test/BuddyTest/App → ./test/BuddyTest/App
绝对路径(/ 或 \\ 开头,含盘符 C:)保持不变。
返回 _mbuddy 分配的字符串;失败时返回原路径。
"""
if path is None:
return None
plen: sizet = string.strlen(path)
if plen == 0:
return path
# 绝对路径(/ 或 \ 开头,或盘符如 C:)不规范化
if path[0] == '/' or path[0] == '\\':
return path
if plen >= 2 and path[1] == ':':
return path
# 输出缓冲(最坏情况 = 输入长度 + 可能的前缀 ./)
out: str = _mbuddy.alloc(plen + 3)
if out is None:
return path
seg_start: sizet = 0
out_len: sizet = 0
has_seg: int = 0
i: sizet = 0
while i <= plen:
ch: char = '\0'
if i < plen:
ch = path[i]
if ch == '/' or ch == '\\' or ch == '\0':
seg_len: sizet = i - seg_start
if seg_len > 0:
# 单段 ".":跳过
if seg_len == 1 and path[seg_start] == '.':
pass
# 单段 "..":弹出上一段;前导 ".." 无段可弹时必须保留(对齐 Python os.path.normpath)
elif seg_len == 2 and path[seg_start] == '.' and path[seg_start + 1] == '.':
# 末段是否已经是前导 ".."(独立一段:要么是开头,要么前面是分隔符)
last_is_dotdot: int = 0
if out_len >= 2 and out[out_len - 1] == '.' and out[out_len - 2] == '.':
if out_len == 2:
last_is_dotdot = 1
elif out[out_len - 3] == '/' or out[out_len - 3] == '\\':
last_is_dotdot = 1
if out_len > 0 and last_is_dotdot == 0:
# 回退到上一个分隔符
k: sizet = out_len
while k > 0 and out[k - 1] != '/' and out[k - 1] != '\\':
k -= 1
if k > 0:
out_len = k - 1
else:
out_len = 0
# 仍有剩余段时保持分隔符逻辑
if out_len > 0:
has_seg = 1
else:
has_seg = 0
else:
# 没有普通段可弹:把 ".." 当普通段追加(前导 .. 累积保留)
if has_seg > 0:
out[out_len] = '/'
out_len += 1
out[out_len] = '.'
out[out_len + 1] = '.'
out_len += 2
has_seg = 1
else:
# 普通段:追加分隔符 + 段
if has_seg > 0:
out[out_len] = '/'
out_len += 1
jj: sizet = 0
while jj < seg_len:
out[out_len] = path[seg_start + jj]
out_len += 1
jj += 1
has_seg = 1
seg_start = i + 1
i += 1
if has_seg == 0:
# 全部段被消掉(如 ./..):返回当前目录
return "./"
# 相对路径补回 ./ 前缀(与原路径风格一致;以 .. 开头的除外)
if out[0] != '.' and out[0] != '/':
# 右移 2 位留出 "./"(从后往前拷贝,避免覆盖源字符)
mv: sizet = out_len
while mv > 0:
mv -= 1
out[mv + 2] = out[mv]
out[0] = '.'
out[1] = '/'
out_len += 2
out[out_len] = '\0'
return out
def resolve_paths(project_dir: str) -> int:
"""解析并规范化工程路径(src/build/includes)
将 project.vpj 中的相对路径(以 ./ 开头)转换为基于 project_dir 的路径。
BuildDir 解析后,TempDir = {BuildDir}/temp,OutputDir = {BuildDir}/output。
Args:
project_dir: 工程根目录(project.vpj 所在目录)
Returns:
0 表示成功,非 0 表示失败
"""
global SourceDir, BuildDir, TempDir, OutputDir, IncludesDir, IncludesDirs
global ProjectDir, LinkerStaticLibs, LinkerDynamicLibs
if project_dir is None:
return 1
if _mbuddy is None:
return 1
ProjectDir = project_dir
SourceDir = _join_path(SourceDir, project_dir)
BuildDir = _join_path(BuildDir, project_dir)
# TempDir = {BuildDir}/temp
if BuildDir is not None:
bd_len: sizet = string.strlen(BuildDir)
temp_buf: str = _mbuddy.alloc(bd_len + 8)
if temp_buf is not None:
viperlib.snprintf(temp_buf, bd_len + 8, "%s/temp", BuildDir)
TempDir = temp_buf
# OutputDir = {BuildDir}/output
output_buf: str = _mbuddy.alloc(bd_len + 8)
if output_buf is not None:
viperlib.snprintf(output_buf, bd_len + 8, "%s/output", BuildDir)
OutputDir = output_buf
# includes 路径可能是 ../includes 形式,需要基于 project_dir 解析
# IncludesDir(首元素)与 IncludesDirs(全部,空格分隔)逐项规范化
if IncludesDirs is not None:
id_total: sizet = string.strlen(IncludesDirs)
id_out: str = _mbuddy.alloc(id_total * 3 + 64)
if id_out is not None:
id_out[0] = '\0'
id_pos: sizet = 0
id_i: sizet = 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: sizet = id_i
while id_i < id_total and IncludesDirs[id_i] != ' ':
id_i += 1
seg_len: sizet = 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: sizet = 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)
# linker.static_libs 显式静态链接文件路径基于 project_dir 逐段解析为绝对路径
if LinkerStaticLibs is not None:
sl_total: sizet = string.strlen(LinkerStaticLibs)
sl_out: str = _mbuddy.alloc(sl_total * 3 + 64)
if sl_out is not None:
sl_out[0] = '\0'
sl_pos: sizet = 0
sl_i: sizet = 0
while sl_i < sl_total:
while sl_i < sl_total and LinkerStaticLibs[sl_i] == ' ':
sl_i += 1
if sl_i >= sl_total:
break
sl_start: sizet = sl_i
while sl_i < sl_total and LinkerStaticLibs[sl_i] != ' ':
sl_i += 1
seg_len: sizet = sl_i - sl_start
if seg_len > 0:
seg_buf: str = _mbuddy.alloc(seg_len + 1)
if seg_buf is not None:
string.strncpy(seg_buf, LinkerStaticLibs + sl_start, seg_len)
seg_buf[seg_len] = '\0'
joined: str = _join_path(seg_buf, project_dir)
if joined is not None:
jl: sizet = string.strlen(joined)
if sl_pos > 0:
sl_out[sl_pos] = ' '
sl_pos += 1
string.strcpy(sl_out + sl_pos, joined)
sl_pos += jl
sl_out[sl_pos] = '\0'
LinkerStaticLibs = sl_out
# linker.dynamic_libs 显式动态链接文件(.dll/.so)同样基于 project_dir
# 逐段解析为绝对路径
if LinkerDynamicLibs is not None:
dl_total: sizet = string.strlen(LinkerDynamicLibs)
dl_out: str = _mbuddy.alloc(dl_total * 3 + 64)
if dl_out is not None:
dl_out[0] = '\0'
dl_pos: sizet = 0
dl_i: sizet = 0
while dl_i < dl_total:
while dl_i < dl_total and LinkerDynamicLibs[dl_i] == ' ':
dl_i += 1
if dl_i >= dl_total:
break
dl_start: sizet = dl_i
while dl_i < dl_total and LinkerDynamicLibs[dl_i] != ' ':
dl_i += 1
seg_len: sizet = dl_i - dl_start
if seg_len > 0:
seg_buf: str = _mbuddy.alloc(seg_len + 1)
if seg_buf is not None:
string.strncpy(seg_buf, LinkerDynamicLibs + dl_start, seg_len)
seg_buf[seg_len] = '\0'
joined: str = _join_path(seg_buf, project_dir)
if joined is not None:
jl: sizet = string.strlen(joined)
if dl_pos > 0:
dl_out[dl_pos] = ' '
dl_pos += 1
string.strcpy(dl_out + dl_pos, joined)
dl_pos += jl
dl_out[dl_pos] = '\0'
LinkerDynamicLibs = dl_out
# linker.flags 中 -T <脚本> 的路径同样基于 project_dir 解析,否则 ld.lld 按进程 cwd 找不到脚本
_resolve_linker_t_paths(project_dir)
return 0
def _join_path_abs(rel: str, project_dir: str) -> str:
"""将相对路径基于 project_dir 拼接为绝对路径(用于 -T 链接脚本等)
已为绝对路径(/ \\ 开头)或盘符形式(x:)时原样返回。
"""
if rel is None:
return None
rel_len: sizet = string.strlen(rel)
if rel_len == 0:
return rel
if rel[0] == '/' or rel[0] == '\\':
return rel
if rel_len >= 2 and rel[1] == ':':
return rel
dir_len: sizet = string.strlen(project_dir)
if dir_len == 0:
return rel
total_len: sizet = dir_len + 1 + rel_len + 1
buf: str = _mbuddy.alloc(total_len)
if buf is None:
return rel
viperlib.snprintf(buf, total_len, "%s/%s", project_dir, rel)
return buf
def _resolve_linker_t_paths(project_dir: str) -> None:
"""将 LinkerFlags 中 `-T <相对路径>` 的链接脚本路径解析为绝对路径
ld.lld 的 -T 参数若是相对路径,将按 subprocess 进程 cwd 解析而找不到
脚本。逐 token 扫描 LinkerFlags,遇 -T 其后 token 若非绝对路径则基于
project_dir 拼接(与 resolve_paths 对其他相对路径的处理保持一致)。
"""
global LinkerFlags
if LinkerFlags is None or project_dir is None:
return
fl_total: sizet = string.strlen(LinkerFlags)
if fl_total == 0:
return
out: str = _mbuddy.alloc(fl_total * 2 + 512)
if out is None:
return
out[0] = '\0'
out_pos: sizet = 0
i: sizet = 0
prev_is_t: int = 0 # 前一 token 是否为 -T
while i < fl_total:
# 跳过前导空格
while i < fl_total and LinkerFlags[i] == ' ':
i += 1
if i >= fl_total:
break
start: sizet = i
while i < fl_total and LinkerFlags[i] != ' ':
i += 1
tok_len: sizet = i - start
is_t_flag: int = 0
if tok_len == 2 and LinkerFlags[start] == '-' and LinkerFlags[start + 1] == 'T':
is_t_flag = 1
# 输出分隔符(前一 token 之后)
if out_pos > 0:
out[out_pos] = ' '
out_pos += 1
if is_t_flag:
string.strcpy(out + out_pos, "-T")
out_pos += 2
elif prev_is_t != 0:
# -T 后的脚本路径:非绝对路径则基于 project_dir 拼接
tok_buf: str = _mbuddy.alloc(tok_len + 1)
if tok_buf is not None:
string.memcpy(tok_buf, LinkerFlags + start, tok_len)
tok_buf[tok_len] = '\0'
abs_path: str = _join_path_abs(tok_buf, project_dir)
if abs_path is not None:
p_len: sizet = string.strlen(abs_path)
string.strcpy(out + out_pos, abs_path)
out_pos += p_len
else:
string.memcpy(out + out_pos, LinkerFlags + start, tok_len)
out_pos += tok_len
else:
string.memcpy(out + out_pos, LinkerFlags + start, tok_len)
out_pos += tok_len
else:
string.memcpy(out + out_pos, LinkerFlags + start, tok_len)
out_pos += tok_len
prev_is_t = is_t_flag
out[out_pos] = '\0'
LinkerFlags = out
def get_includes_binary_dir() -> str:
"""获取 includes.binary 目录路径(includes 目录 + 缓存后缀)
includes 目录如 ../includes,默认返回 ../includes.binary;
asan 模式(set_inc_bin_suffix(".binary_asan"))返回 ../includes.binary_asan,
隔离 asan 插桩 obj,避免与非 asan obj 混链。
"""
if IncludesDir is None:
return None
inc_len: sizet = string.strlen(IncludesDir)
suf_len: sizet = string.strlen(_inc_bin_suffix)
buf: str = _mbuddy.alloc(inc_len + suf_len + 1)
if buf is None:
return None
viperlib.snprintf(buf, inc_len + suf_len + 1, "%s%s", IncludesDir, _inc_bin_suffix)
return buf
def asan_runtime_declared() -> int:
"""project.vpj 是否已声明 ASan 运行库(1 = 声明过)
TPV 不再硬编码 clang64 的 libclang_rt 路径:clang 没有给
x86_64-w64-windows-gnu 目标出 ASan 运行库,只能借用 clang64(MSVC ABI)
的那两份(会牵进 MSVC 专有的 .drectve 链接指令),这是工程级选择,
必须由工程自己声明。
三种写法都认(和 openssl 那种「写在 flags 里」的写法兼容):
* linker.static_libs 里的 .a / .dll.a
* linker.dynamic_libs 里的 .dll / .so
* linker.flags 里的 -L… -l:…(如 openssl 的 -lssl -lcrypto)
判定方式:以上任一处出现 "asan" 即视为已声明。
"""
if LinkerStaticLibs is not None:
if string.strstr(LinkerStaticLibs, "asan") is not None:
return 1
if LinkerDynamicLibs is not None:
if string.strstr(LinkerDynamicLibs, "asan") is not None:
return 1
if LinkerFlags is not None:
if string.strstr(LinkerFlags, "asan") is not None:
return 1
return 0
def print_config():
"""打印当前配置"""
if ProjectName is not None:
VLogger.info(f"project: {ProjectName} v{ProjectVersion if ProjectVersion is not None else "?"}", "config")
if SourceDir is not None:
VLogger.info(f"source_dir: {SourceDir}", "config")
if BuildDir is not None:
VLogger.info(f"build_dir: {BuildDir}", "config")
if TempDir is not None:
VLogger.info(f"temp_dir: {TempDir}", "config")
if OutputDir is not None:
VLogger.info(f"output_dir: {OutputDir}", "config")
if CompilerCmd is not None:
VLogger.info(f"compiler: {CompilerCmd}", "config")
if LinkerCmd is not None:
VLogger.info(f"linker: {LinkerCmd} -> {LinkerOutput if LinkerOutput is not None else "?"}", "config")
if IncludesDir is not None:
VLogger.info(f"includes: {IncludesDir}", "config")
VLogger.info(f"sha1_slice_level: {Sha1SliceLevel}", "config")
# ============================================================
# slice_subdir - 每层取 SHA1 的 2 字符作为子目录名(level=0 不切片, 1→"b7", 2→"b7/90")
# Args: sha1(至少 2*level 字符)/ level(切片层数)
# Returns: 子目录路径字符串(mbuddy 分配,无需释放),level<=0 时返回 None
# ============================================================
def slice_subdir(sha1: str, level: int) -> str:
"""根据 SHA1 和切片层数返回子目录路径"""
if level <= 0 or sha1 is None:
return None
if _mbuddy is None:
return None
# 每层 2 字符 + 分隔符,最后无分隔符 + null
# buf 大小: level * 2 + (level - 1) + 1 = level * 3
buf_len: sizet = sizet(level * 3)
buf: str = _mbuddy.alloc(buf_len)
if buf is None:
return None
pos: int = 0
i: int = 0
while i < level:
if i > 0:
buf[pos] = '/'
pos += 1
idx: int = i * 2
buf[pos] = sha1[idx]
buf[pos + 1] = sha1[idx + 1]
pos += 2
i += 1
buf[pos] = '\0'
return buf