3、【MOD开发】【骑砍2】跨平台MOD构建环境(三):只读源码同步、子模块与路径大小写

【声明】本博客所有内容均为个人业余时间创作,所述技术案例均来自公开开源项目(如Github,Apache基金会),不涉及任何企业机密或未公开技术,如有侵权请联系删除

3、【MOD开发】【骑砍2】跨平台MOD构建环境(三):只读源码同步、子模块与路径大小写

背景

上篇 【MOD开发】【骑砍2】跨平台MOD构建环境(二):参考程序集与伪游戏目录 解决了「游戏依赖从哪来」:用 NuGet 包 bannerlord.referenceassemblies.<suffix> 提供参考程序集(只含元数据、不含 IL 程序体),由装配脚本的 map_ref_dir 把包后缀还原成真实目录,装配出一个「伪游戏目录」,并用 留档 → 缓存 → 在线 三级优先级加 refs.lock.json 里的 SHA-256 校验和保证离线可复现;其中 core 包缺失会直接终止,其余后缀不可用只告警跳过。

SDK 与参考程序集都齐了,接下来要面对的是源码本身 :源码要拷到哪、子模块怎么拉、Windows 工程里那些「大小写随意」的路径怎么在 Linux 上编得过。这正是流水线第三步 sync.sh 的职责,本篇讲清它。


骑砍2

🎯 为什么再拷一份源码

整条流水线有一条贯穿始终的原则:源码目录只读,绝不写入 。所有同步、克隆、打补丁、改路径都在 work/<id>/ 的副本里进行。好处是:源仓库保持干净(不被构建产物、子模块、补丁污染),删掉工作区就能一键回到初始状态。

sync.sh 是四步流水线,输入是只读源码,输出是构建副本。


📦 rsync 同步与排除项

第一步用 rsync 同步,关键是 --delete(保证副本与源一致)配合一批排除项,避免把构建产物或子模块内容误删/误拷。

sh 复制代码
# sync.sh:同步源码到构建副本(示例)
rsync -a --delete \
	--exclude '.git' \
	--exclude 'bin' \
	--exclude 'obj' \
	--exclude 'package' \
	$_excl \
	"$_src" "$_dst"

各排除项的作用:

排除项 作用
.git 不把源仓库的版本库带进副本
bin / obj 丢弃旧的编译产物,避免「假通过」(见第四篇)
package 丢弃旧的打包结果
$_excl(子模块路径) 专为 --delete 准备:防止已克隆的子模块被当成「源里没有的文件」而被删掉

最后一项尤其关键:子模块是构建时克隆进来的,源仓库里并没有对应内容;若不排除,rsync --delete 会把刚克隆的子模块目录清空。实现上,脚本会读取描述符里 submodules[].path,把它们拼成 --exclude=/<相对路径>。


🧩 子模块克隆:只认 commit,不因 dirty 重克隆

第二步按描述符里的 url + commit 克隆子模块。判断「是否已就绪」时只看 HEAD 是否等于目标 commit:

sh 复制代码
# sync.sh:子模块就绪判定(示例)
_head=$(git -C "$_target" rev-parse HEAD 2>/dev/null || echo "")
# 只校验 commit;工作区可能被 patches/ 有意修改(dirty),不因此重克隆
if [ "$_head" = "$_commit" ]; then
	_ready=1
fi

这里有个刻意的取舍:工作区被补丁改脏(dirty)不算「需要重克隆」 。因为补丁会在克隆后修改文件,如果设定成「dirty 就重克隆」,那每次构建都会把补丁改动冲掉、再重打一遍,既浪费也可能反复触发。只要 commit 对,就认为子模块就绪。


🩹 patches:给不便整仓 fork 的代码打补丁

第三步把 patches/*.patch 应用到构建副本。它主要用于子模块这类不便整仓 fork 的代码(子模块归上游仓库所有,本地改动用补丁表达更干净)。

sh 复制代码
# sync.sh:幂等打补丁(示例)
if patch -p1 -l -d "$_buildroot" --forward --no-backup-if-mismatch \
	-i "$_p" >/dev/null 2>&1; then
	info "已应用补丁: $(basename "$_p")"
else
	debug "补丁无需应用(已应用或不匹配): $(basename "$_p")"
fi

--forward 让「已打过」的补丁被安全跳过而不报错,从而整个流程幂等:重复构建不会因为补丁重复应用而失败。


🔤 跨平台路径大小写

第四步是这套环境能在 Linux 编译 Windows 工程的关键之一 。原因很直接:Bannerlord 的 MOD 多在 Windows 上用 Visual Studio 开发,工程文件里的路径大小写往往与磁盘实际不一致------Windows 文件系统不区分大小写 ,所以 Source\Foo.cs 与 source/foo.cs 都能找到文件;而 Linux 文件系统区分大小写 ,一旦大小写对不上就是 CS2001: Source file ... could not be found。

路径修正脚本的策略是「只修正确定能修、且不冒险的」:

核心判定:原路径直接存在就不动;原路径不存在、且存在大小写不敏感的唯一匹配时才改写。

python 复制代码
# pathcase.sh 内部:单条相对路径的判定(示例)
direct = os.path.join(base, *split_rel(rel))
if os.path.exists(direct):
    return None                 # 大小写正确,无需处理
if ci_resolve(base, rel) is None:
    return None                 # 找不到唯一匹配,不改(避免误伤)
# 逐段用 os.listdir 的大小写不敏感匹配,还原真实大小写

两条设计要点值得强调:

维度 直接按小写改 条件改写
触发条件 无差别 仅当原路径不存在且存在唯一匹配
风险 可能把本就正确的路径改错 几乎不可能误伤
匹配方式 字符串替换 逐段大小写不敏感比对

一个具体例子

假设工程里写了 Compile Include="Source\Models\ItemVM.cs",而磁盘上的真实目录是 source/models/ItemVM.cs。两者在 Linux 下的处理对照如下:

环节 结果
原路径 Source/Models/ItemVM.cs 是否存在 不存在(Linux 区分大小写)
大小写不敏感的逐段匹配 Source→source、Models→models,唯一匹配
判定与动作 原不存在 + 唯一匹配 → 改写为 source/models/ItemVM.cs
改写后 文件可被 MSBuild 找到,不再 CS2001

反过来,若工程里写的路径本来就正确,第一步「原路径直接存在」就命中,脚本原样放行。修正脚本处理两类路径:$(MSBuildThisFileDirectory) 后面的相对路径,以及引号里的、带已知扩展名且不含 $( 与 :// 的路径;它只改构建副本,源仓库保持原样。


🧵 四步的顺序

四个动作由 sync.sh 串起来,顺序固定:

sh 复制代码
# sync.sh:依次执行四步(示例)
sync_source        # rsync 到构建副本
clone_submodules   # 按 url+commit 克隆
apply_patches      # 应用 patches/*.patch
fix_path_case      # 修正工程文件路径大小写

对应到使用就是一条命令:./sync.sh <mod> config/<mod>.json。它既可以被主入口调用,也能在调试时单独执行,便于排查「是同步阶段还是编译阶段出的问题」。

一句话记忆:源只读;子模块认 commit 不认 dirty;补丁 --forward 幂等;路径大小写只在「原不存在 + 唯一匹配」时才改。


📌 本篇小结

  • sync.sh 四步:rsync 副本 → 克隆子模块 → 打补丁 → 修正路径大小写,全部在 work/<id>/ 副本里做。
  • rsync --delete 必须排除子模块路径,否则会清空已克隆的子模块。
  • 子模块就绪只看 commit;工作区 dirty(补丁所致)不触发重克隆。
  • patches/ 用 --forward 保证幂等。
  • 路径修正用「原路径不存在 + 大小写不敏感唯一匹配」的安全条件,解决 Windows 工程在 Linux 上编译的路径大小写问题。

下一篇进入流水线最后两步:跨平台编译(EnableWindowsTargeting、GamePath 注入、覆盖 Windows 专属打包)与按 Bannerlord 模块布局打包。


OK,本篇先到这里,如有疑问,欢迎评论区留言讨论,祝各位功力大涨,技术更上一层楼!!!更多内容见下篇 blog

相关推荐
HIT_Weston1 天前
1、【MOD开发】【骑砍2】跨平台MOD构建环境(一):总体思路与 SDK 引导
mod开发·骑砍2
霸王•吕布3 年前
骑砍战团MOD开发(39)-RTS塔防保卫卡拉迪亚大陆
游戏开发·骑马与砍杀·骑砍战团·mod开发·rts塔防·rts视角·rts游戏