diff --git a/App/lib/Phase/BuildPipeline.vp b/App/lib/Phase/BuildPipeline.vp index 3d14767..11786e8 100644 --- a/App/lib/Phase/BuildPipeline.vp +++ b/App/lib/Phase/BuildPipeline.vp @@ -719,7 +719,7 @@ def collect_obj_files(includes_binary_dir: str, out_buf: bytes, out_size: t.CSiz def link_obj_to_exe(output_dir: str, module_name: str, linker_cmd: str, linker_flags: str, linker_output: str, - includes_binary_dir: str, linker_static_libs: str = None) -> int: + includes_binary_dir: str, linker_static_libs: str = None, linker_dynamic_libs: str = None) -> int: """调用 clang++ 链接 .obj 为 .exe Args: @@ -730,6 +730,7 @@ def link_obj_to_exe(output_dir: str, module_name: str, linker_cmd: str, linker_f linker_output: 输出文件名(如 "test.exe") includes_binary_dir: includes.binary 目录路径(链接时附加预编译 .obj) linker_static_libs: 显式静态链接文件(.a/.obj,空格分隔绝对路径,可为 None) + linker_dynamic_libs: 显式动态链接文件(.dll/.so,空格分隔绝对路径,可为 None) Returns: 0 成功,非 0 失败 @@ -755,9 +756,10 @@ def link_obj_to_exe(output_dir: str, module_name: str, linker_cmd: str, linker_f else: VLogger.warning(f"includes.binary 无 .obj 文件: {includes_binary_dir}", "link") - # 构造命令: clang++ {obj_path} extra_objs [static_libs] -o {output_dir}/{linker_output} linker_flags + # 构造命令: clang++ {obj_path} extra_objs [static_libs/dynamic_libs] -o {output_dir}/{linker_output} linker_flags # 注意: .obj 文件必须在 -l 库标志之前,否则链接器无法解析符号依赖 - # linker_static_libs(project.vpj 显式指定的 .a/.obj)同样位于 -l 之前 + # linker_static_libs / linker_dynamic_libs(project.vpj 显式指定的 .a/.obj/.dll/.so) + # 同样位于 -l 之前;动态库作为输入文件由 GNU ld 提取导入符号 # ASan 模式:mingw64 clang 的 -fsanitize=address 在 mingw64/lib 下找不到 # asan 运行库(实际在 clang64),改为显式链接 clang64 的 libclang_rt # (DLL 版,运行时需 D:/msys64/clang64/bin 在 PATH) @@ -773,6 +775,18 @@ def link_obj_to_exe(output_dir: str, module_name: str, linker_cmd: str, linker_f static_libs: str = linker_static_libs if static_libs is None: static_libs = "" + if linker_dynamic_libs is not None and len(linker_dynamic_libs) > 0: + # 合并 static + dynamic 为单个空格分隔字符串(Viper str += 拼接 + # 会生成非法 add i8* IR,须用 snprintf 构造) + merged_sl: str = _mbuddy.alloc(len(static_libs) + len(linker_dynamic_libs) + 2) + if merged_sl is not None: + if len(static_libs) > 0: + viperlib.snprintf(merged_sl, len(static_libs) + len(linker_dynamic_libs) + 2, + "%s %s", static_libs, linker_dynamic_libs) + else: + viperlib.snprintf(merged_sl, len(static_libs) + len(linker_dynamic_libs) + 2, + "%s", linker_dynamic_libs) + static_libs = merged_sl sl_len: t.CSizeT = len(static_libs) cmd_len: t.CSizeT = len(linker_cmd) + len(obj_path) + len(use_flags) + len(output_dir) + len(linker_output) + extra_len + sl_len + 128 cmd: bytes = _mbuddy.alloc(cmd_len) @@ -912,7 +926,7 @@ def compile_module_to_obj(ir_buf: bytes, ir_len: t.CSizeT, def link_objs_to_exe(obj_paths: str, obj_paths_len: t.CSizeT, linker_cmd: str, linker_flags: str, linker_output: str, - includes_binary_dir: str, linker_static_libs: str = None) -> int: + includes_binary_dir: str, linker_static_libs: str = None, linker_dynamic_libs: str = None) -> int: """链接多个 .obj 文件为 .exe Args: @@ -1008,6 +1022,17 @@ def link_objs_to_exe(obj_paths: str, obj_paths_len: t.CSizeT, static_libs2: str = linker_static_libs if static_libs2 is None: static_libs2 = "" + if linker_dynamic_libs is not None and len(linker_dynamic_libs) > 0: + # 合并 static + dynamic(Viper str += 拼接生成非法 IR,用 snprintf) + merged_sl2: str = _mbuddy.alloc(len(static_libs2) + len(linker_dynamic_libs) + 2) + if merged_sl2 is not None: + if len(static_libs2) > 0: + viperlib.snprintf(merged_sl2, len(static_libs2) + len(linker_dynamic_libs) + 2, + "%s %s", static_libs2, linker_dynamic_libs) + else: + viperlib.snprintf(merged_sl2, len(static_libs2) + len(linker_dynamic_libs) + 2, + "%s", linker_dynamic_libs) + static_libs2 = merged_sl2 sl2_len: t.CSizeT = len(static_libs2) cmd_len: t.CSizeT = len(linker_cmd) + obj_paths_len + len(use_flags) + len(linker_output) + extra_len + sl2_len + 128 cmd: bytes = _mbuddy.alloc(cmd_len) @@ -1057,7 +1082,7 @@ def run_pipeline(ir_buf: bytes, ir_len: t.CSizeT, temp_dir: str, output_dir: str, module_name: str, cc_cmd: str, cc_flags: str, linker_cmd: str, linker_flags: str, linker_output: str, - includes_binary_dir: str, linker_static_libs: str = None) -> BuildResult | t.CPtr: + includes_binary_dir: str, linker_static_libs: str = None, linker_dynamic_libs: str = None) -> BuildResult | t.CPtr: """执行完整编译管线 Args: @@ -1106,9 +1131,9 @@ def run_pipeline(ir_buf: bytes, ir_len: t.CSizeT, result.ErrorMsg = "llc 编译失败" return result - # Step 3: clang++ 链接 .obj → .exe(附加 includes.binary 预编译 .obj 与 static_libs) + # Step 3: clang++ 链接 .obj → .exe(附加 includes.binary 预编译 .obj 与 static_libs/dynamic_libs) ret = link_obj_to_exe(output_dir, module_name, linker_cmd, linker_flags, linker_output, - includes_binary_dir, linker_static_libs) + includes_binary_dir, linker_static_libs, linker_dynamic_libs) if ret != 0: result.Success = 0 result.ErrorMsg = "链接失败" diff --git a/App/lib/Phase/Phase2/__init__.vp b/App/lib/Phase/Phase2/__init__.vp index 67f8397..16b770e 100644 --- a/App/lib/Phase/Phase2/__init__.vp +++ b/App/lib/Phase/Phase2/__init__.vp @@ -763,7 +763,8 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, do_phase1: int, do_phase2: int, log: VLogger.Logger | t.CPtr, args: argparse.ParsedArgs | t.CPtr, - linker_static_libs: str = None) -> int: + linker_static_libs: str = None, + linker_dynamic_libs: str = None) -> int: """多文件项目编译 Returns: 0 成功,非 0 失败 @@ -2014,7 +2015,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, lret: int = BuildPipeline.link_objs_to_exe( final_obj_paths, obj_paths_len, linker_cmd, linker_flags, exe_path, - None, linker_static_libs) + None, linker_static_libs, linker_dynamic_libs) if lret == 0: VLogger.success(f"输出: {exe_path}", "PhaseC") diff --git a/App/lib/Projectrans/Config.vp b/App/lib/Projectrans/Config.vp index 0102902..573132e 100644 --- a/App/lib/Projectrans/Config.vp +++ b/App/lib/Projectrans/Config.vp @@ -34,6 +34,7 @@ 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 设置) @@ -58,7 +59,7 @@ def Load_project_config(path: str) -> int: 0 表示成功,非 0 表示失败 """ global SourceDir, BuildDir, TempDir, OutputDir, ProjectName, ProjectVersion - global CompilerCmd, CompilerFlags, LinkerCmd, LinkerFlags, LinkerOutput, LinkerStaticLibs + global CompilerCmd, CompilerFlags, LinkerCmd, LinkerFlags, LinkerOutput, LinkerStaticLibs, LinkerDynamicLibs global TargetTriple, TargetDataLayout, Sha1SliceLevel, StrictMode global IncludesDir, IncludesDirs @@ -124,6 +125,10 @@ def Load_project_config(path: str) -> int: # 先拼接原样字符串,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"] @@ -397,7 +402,7 @@ def resolve_paths(project_dir: str) -> int: 0 表示成功,非 0 表示失败 """ global SourceDir, BuildDir, TempDir, OutputDir, IncludesDir, IncludesDirs - global ProjectDir, LinkerStaticLibs + global ProjectDir, LinkerStaticLibs, LinkerDynamicLibs if project_dir is None: return 1 if _mbuddy is None: @@ -488,6 +493,39 @@ def resolve_paths(project_dir: str) -> int: 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: t.CSizeT = string.strlen(LinkerDynamicLibs) + dl_out: str = _mbuddy.alloc(dl_total * 3 + 64) + if dl_out is not None: + dl_out[0] = '\0' + dl_pos: t.CSizeT = 0 + dl_i: t.CSizeT = 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: t.CSizeT = dl_i + while dl_i < dl_total and LinkerDynamicLibs[dl_i] != ' ': + dl_i += 1 + seg_len: t.CSizeT = 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: t.CSizeT = 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 解析, # 否则 subprocess 按进程 cwd(TransPyC/)找 linker.ld → ld.lld 报 # "cannot find linker script linker.ld" diff --git a/App/main.vp b/App/main.vp index ca1b604..2041ab9 100644 --- a/App/main.vp +++ b/App/main.vp @@ -369,13 +369,14 @@ Target: %s mf_linker_flags: str = Config.LinkerFlags if Config.LinkerFlags is not None else "-lmsvcrt -lucrt -lpthread -lmingwex -lkernel32 -lgcc -Wl,--allow-multiple-definition" mf_linker_out: str = Config.LinkerOutput if Config.LinkerOutput is not None else "app.exe" mf_static_libs: str = Config.LinkerStaticLibs + mf_dynamic_libs: str = Config.LinkerDynamicLibs mf_includes_bin: str = Config.get_includes_binary_dir() 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.IncludesDirs, do_phase1, do_phase2, log, args, - mf_static_libs) + mf_static_libs, mf_dynamic_libs) argparse.release(args) log.success("TransPyV 完成") return mf_ret @@ -494,7 +495,8 @@ Target: %s cc_flags: str = "-filetype=obj -relocation-model=pic" linker_cmd: str = Config.LinkerCmd if Config.LinkerCmd is not None else "clang++" linker_flags: str = "-lmsvcrt -lucrt -lpthread -lmingwex -lkernel32 -lgcc -Wl,--allow-multiple-definition" - linker_static_libs: str = Config.LinkerStaticLibs + linker_static_libs: str = Config.LinkerStaticLibs + linker_dynamic_libs: str = Config.LinkerDynamicLibs # --project 模式且未显式指定 --src 时:使用 config 中的 linker_output(如 TransPyV.exe) # 显式指定 --src 时:使用从文件名推导的 output_name(如 test.exe),避免覆盖正在运行的 exe if project is not None and Config.LinkerOutput is not None and args.get_str("src") is None: @@ -566,7 +568,7 @@ Target: %s br: BuildPipeline.BuildResult | t.CPtr = BuildPipeline.run_pipeline( final_ir, final_ir_len, temp_dir, output_dir, module_name, cc_cmd, cc_flags, linker_cmd, linker_flags, linker_output, - includes_binary_dir, linker_static_libs + includes_binary_dir, linker_static_libs, linker_dynamic_libs ) if br is not None and br.Success == 1: log.success("编译管线完成")