在 WSL2 中编译 ONLYOFFICE Core 模块的踩坑记录
编译日期:2026-09-24
编译目标:ONLYOFFICE Core 模块(python3 ./automate.py core --update=0)
环境信息
硬件
操作系统
编译工具链
仓库版本
sysroot 信息
坑一:中断后无法重新触发 v8 初始化
现象
中断了 core/Common/3dParty 的初始化后,重新运行 automate.py core,发现 v8 相关目录已经存在但状态不完整,脚本不会重新初始化。
原因
v8_89.py 的 make() 函数通过以下方式判断是否跳过初始化:
v8.data文件 — 内容为v8_version_1,作为版本标记。存在则认为已初始化。if not base.is_dir("v8")— v8 目录存在则跳过fetch/gclient sync。
中断后目录可能残留但内容不完整,脚本不会重新初始化。
解决
删除版本标记文件 v8.data 即可触发完全重新初始化(clean() 会删除 depot_tools、v8、.gclient 等):
rm -f /mnt/wsl/onlyoffice/core/Common/3dParty/v8_89/v8.data
注意:同级的 core/Common/3dParty/v8/ 目录是 core 仓库自带的旧版 v8 项目文件(.gitignore、android/、v8_xp/、v8.pri),与 v8_89 无关,不需要删除。
坑二:depot_tools 的 Python 3 版本不兼容
现象
python3_bin_reldir.txt not found. need to initialize depot_tools by
running gclient, update_depot_tools or ensure_bootstrap.
或
AttributeError: module 'argparse' has no attribute 'BooleanOptionalAction'
原因
depot_tools/fetch.py 使用了 argparse.BooleanOptionalAction,这是 Python 3.9+ 才有的 API。而 change_bootstrap() 原本在 Linux 上使用 3.8.10.chromium.23 版本的 CPython。
解决
修改 v8_89.py 的 change_bootstrap() 函数,将 Linux 的 Python 版本也改为 3.11.8.chromium.35(与 Windows 一致):
content += "infra/3pp/tools/cpython3/${platform} version:2@3.11.8.chromium.35`n`n"
坑三:depot_tools python3 符号链接导致路径解析错误
现象
手动执行 fetch v8 时报 python3_bin_reldir.txt not found,但文件实际存在。
原因
v8_89.py 原代码用符号链接创建 depot_tools/python3:
base.cmd("ln", ["-sf", "python-bin/python3", "./depot_tools/python3"])
但 python-bin/python3 脚本内部使用 DEPOT_TOOLS=$(dirname "$0")/.. 定位自身目录:
直接执行
depot_tools/python-bin/python3:$0=depot_tools/python-bin/python3→DEPOT_TOOLS=depot_tools✅通过符号链接
depot_tools/python3:$0=depot_tools/python3→DEPOT_TOOLS=depot_tools/..=v8_89❌
解决
不用符号链接,改用包装脚本(wrapper script),通过(dirname "$0")" && pwd) 获取绝对路径:
wrapper = "#!/usr/bin/env bash" + chr(10)
wrapper += 'exec "$(cd "$(dirname "$0")" && pwd)/python-bin/python3" "$@"' + chr(10)
base.writeFile("./depot_tools/python3", wrapper)
base.cmd("chmod", ["+x", "./depot_tools/python3"])
坑四:ensure_bootstrap 执行顺序错误
现象
fetch v8 报错 python3_bin_reldir.txt not found,因为 bootstrap 还没执行。
原因
v8_89.py 原代码的执行顺序:
1. clone depot_tools + change_bootstrap()
2. fetch v8 ← 需要 python3,但还没 bootstrap!
3. gclient sync
4. ensure_bootstrap ← 太晚了,在 fetch 之后
解决
将 ensure_bootstrap 移到 fetch v8 之前,同时设置 DEPOT_TOOLS_UPDATE=0 防止 depot_tools 自动更新覆盖 change_bootstrap() 的修改:
base.set_env("DEPOT_TOOLS_UPDATE", "0")
if ("linux" == base.host_platform()):
if not base.is_file("./depot_tools/python3_bin_reldir.txt"):
base.cmd_in_dir("./depot_tools", "./ensure_bootstrap", [], True)
if not base.is_exist("./depot_tools/python3"):
# 创建包装脚本(见坑三)
...
if not base.is_dir("v8"):
base.cmd("./depot_tools/fetch", ["v8"], True)
...
坑五:gclient sync 并行下载导致锁文件冲突
现象
BlockingIOError: [Errno 11] Resource temporarily unavailable
lockfile.LockError: Error locking .../gsutil_5.35 (err: [Errno 11] Resource temporarily unavailable)
原因
gclient sync 默认多线程并行执行,多个 gsutil 进程争抢锁文件。在 WSL 环境下文件锁行为可能不稳定。
解决
给 gclient sync 加 -j1 参数强制单线程:
base.cmd("./depot_tools/gclient", ["sync", "-r", v8_branch_version, "-j1"], True)
base.cmd("gclient", ["sync", "--force", "-j1"], True)
如果已经产生了锁文件残留,手动删除:
rm -f depot_tools/external_bin/gsutil/gsutil_5.35.locked
坑六:download_from_google_storage 找不到
现象
FileNotFoundError: [Errno 2] No such file or directory: 'download_from_google_storage'
原因
手动运行 gclient sync 时 depot_tools 不在 PATH 中,hooks 无法找到 download_from_google_storage 脚本。
解决
手动运行 gclient sync 时需要将 depot_tools 加入 PATH:
cd /mnt/wsl/onlyoffice/core/Common/3dParty/v8_89
export DEPOT_TOOLS_UPDATE=0
export PATH="$PWD/depot_tools:$PATH"
./depot_tools/gclient sync -r "remotes/branch-heads/8.9" -j1
坑七:ninja 二进制缺失
现象
depot_tools/ninja.py: Could not find Ninja in the third_party of the current project, nor in your PATH.
原因
depot_tools/ninja.py 在 v8/third_party/ninja/ninja 查找 ninja 二进制文件。正常流程中 v8_89.py 会备份恢复 ninja:
base.copy_dir("./v8/third_party_new/ninja", "./v8/third_party/ninja")
但如果手动执行了 fetch/sync(跳过了这步),或 gclient sync 的 CIPD 没有正确下载 ninja,该目录就会为空。
解决
手动从 v8/third_party/depot_tools/ 复制 ninja 二进制:
mkdir -p v8/third_party/ninja
cp v8/third_party/depot_tools/ninja-linux64 v8/third_party/ninja/ninja
chmod +x v8/third_party/ninja/ninja
坑八:sysroot 的 ld.gold 不支持 .so 脚本中的 = 前缀
现象
ld.gold: error: lib/x86_64-linux-gnu/libpthread.so:5:9: syntax error, unexpected '=', expecting '-' or STRING or QUOTED_STRING or AS_NEEDED
ld.gold: error: lib/x86_64-linux-gnu/libpthread.so: not an object or archive
大量 undefined reference to 'strlen'、'memcpy'、'pthread_mutex_init' 等链接错误。
原因
sysroot 中的 .so 文件是 GNU ld 链接器脚本,使用 = 前缀表示 sysroot 相对路径:
GROUP ( =/lib/x86_64-linux-gnu/libpthread.so.0 =/lib/x86_64-linux-gnu/libpthread_nonshared.a )
但 sysroot 自带的 ld.gold 2.26.1(v1.11)不支持 = 前缀语法。这是 ld.bfd 专有语法。
尝试过的失败方案
最终解决
保留 sysroot 原始的 ld.gold(支持 --threads),去掉 .so 脚本中的 = 前缀(gold 在 --sysroot 模式下会自动补全路径):
cd /mnt/wsl/onlyoffice/build_tools/tools/linux/sysroot/ubuntu16-amd64-sysroot
# 恢复原始 gold(如果之前被替换过)
cp usr/bin/x86_64-linux-gnu-ld.gold.bak usr/bin/x86_64-linux-gnu-ld.gold
# 去掉三个 .so 脚本中的 = 前缀
sed -i 's/=\//\//g' lib/x86_64-linux-gnu/libpthread.so
sed -i 's/=\//\//g' lib/x86_64-linux-gnu/libm.so
sed -i 's/=\//\//g' lib/x86_64-linux-gnu/libc.so
坑九:sysroot 的 libstdc++ 版本过低
现象
cmake: .../libstdc++.so.6: version `GLIBCXX_3.4.26' not found (required by cmake)
cmake: .../libstdc++.so.6: version `GLIBCXX_3.4.22' not found (required by cmake)
原因
编译 heif 模块时 set_sysroot_env() 将 sysroot 的 bin 目录加到 PATH 最前面,cmake 加载了 sysroot 的 libstdc++.so.6(6.0.21,最高 GLIBCXX_3.4.21),但系统安装的 cmake 需要更高版本(GLIBCXX_3.4.26+)。
解决
将 sysroot 的 libstdc++.so.6 指向系统的版本(与 v8_89.py 中 fix_ubuntu24() 做法一致):
cd /mnt/wsl/onlyoffice/build_tools/tools/linux/sysroot/ubuntu16-amd64-sysroot/usr/lib/x86_64-linux-gnu
mv libstdc++.so.6 libstdc++.so.6.old
ln -s /usr/lib/x86_64-linux-gnu/libstdc++.so.6 libstdc++.so.6
坑十:系统 cmake 版本过低不支持 --preset
现象
CMake Error: The source directory ".../libheif/build/linux_64/release/--preset=release-noplugins" does not exist.
原因
heif 模块编译时使用 --preset=release-noplugins 参数(CMake Presets),这是 CMake 3.19+ 的功能。但 Ubuntu 20.04 自带的 cmake 是 3.16.3,不认识 --preset,把它当成了源目录路径。
解决
通过 pip 安装新版 cmake:
pip3 install cmake --upgrade
hash -r # 刷新 shell 命令缓存
cmake --version # 确认是 3.28+
坑十一:JAVA_HOME 未设置导致 jni.h 找不到
现象
docbuilder_CDocBuilder.h:37:17: fatal error: jni.h: No such file or directory
#include <jni.h>
^
Warning: environment variable JAVA_HOME wasn't set. Default Java compiler will be used (if any).
原因
编译 docbuilder 的 Java JNI 绑定时需要 jni.h 头文件。系统已安装 OpenJDK 11(jni.h 存在于 /usr/lib/jvm/java-11-openjdk-amd64/include/jni.h),但 JAVA_HOME 环境变量未设置,编译脚本找不到 JDK 路径。
解决
设置 JAVA_HOME 环境变量后重新编译:
# 临时生效
export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64
# 永久生效
echo 'export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64' >> ~/.bashrc
source ~/.bashrc
# 重新编译
cd /mnt/wsl/onlyoffice/build_tools/tools/linux
python3 ./automate.py builder --update=0 --clean=1
附录 A:所有环境修复命令汇总
# ===== 1. sysroot 链接器修复 =====
cd /mnt/wsl/onlyoffice/build_tools/tools/linux/sysroot/ubuntu16-amd64-sysroot
# 恢复原始 ld.gold(支持 --threads)
cp usr/bin/x86_64-linux-gnu-ld.gold.bak usr/bin/x86_64-linux-gnu-ld.gold
# 去掉 .so 脚本中的 = 前缀(gold 不认 = 语法)
sed -i 's/=\//\//g' lib/x86_64-linux-gnu/libpthread.so
sed -i 's/=\//\//g' lib/x86_64-linux-gnu/libm.so
sed -i 's/=\//\//g' lib/x86_64-linux-gnu/libc.so
# ===== 2. sysroot libstdc++ 修复 =====
cd usr/lib/x86_64-linux-gnu
mv libstdc++.so.6 libstdc++.so.6.old
ln -s /usr/lib/x86_64-linux-gnu/libstdc++.so.6 libstdc++.so.6
# ===== 3. 设置 JAVA_HOME =====
export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64
echo 'export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64' >> ~/.bashrc
# ===== 4. cmake 升级 =====
pip3 install cmake --upgrade
hash -r
# ===== 5. 重新编译 =====
cd /mnt/wsl/onlyoffice/build_tools/tools/linux
python3 ./automate.py core --update=0
附录 B:v8_89.py 代码修改汇总
以下修改均在 scripts/core_common/modules/v8_89.py 中进行。
修改 1:change_bootstrap() — Python 版本统一
# Linux 和 Windows 都使用 3.11.8.chromium.35
content += "infra/3pp/tools/cpython3/${platform} version:2@3.11.8.chromium.35`n`n"
修改 2:make() — 初始化顺序调整
# 防止 depot_tools 自动更新覆盖 change_bootstrap() 的修改
base.set_env("DEPOT_TOOLS_UPDATE", "0")
# 在 fetch 之前完成 bootstrap(原代码在 fetch 之后才执行)
if ("linux" == base.host_platform()):
if not base.is_file("./depot_tools/python3_bin_reldir.txt"):
base.cmd_in_dir("./depot_tools", "./ensure_bootstrap", [], True)
# 创建 python3 包装脚本(非符号链接,避免 $0 路径解析错误)
if not base.is_exist("./depot_tools/python3"):
wrapper = "#!/usr/bin/env bash" + chr(10)
wrapper += 'exec "$(cd "$(dirname "$0")" && pwd)/python-bin/python3" "$@"' + chr(10)
base.writeFile("./depot_tools/python3", wrapper)
base.cmd("chmod", ["+x", "./depot_tools/python3"])
if not base.is_dir("v8"):
base.cmd("./depot_tools/fetch", ["v8"], True)
...
# gclient sync 加 -j1 避免并行锁冲突
base.cmd("./depot_tools/gclient", ["sync", "-r", v8_branch_version, "-j1"], True)
base.cmd("gclient", ["sync", "--force", "-j1"], True)
修改 3:make() — ninja 存在性检查(可选)
在 fetch/sync 块之后、平台特定补丁之前添加:
# 确保 ninja 二进制存在(gclient sync 的 CIPD 可能未正确下载 ninja)
if not base.is_file("v8/third_party/ninja/ninja"):
base.create_dir("v8/third_party/ninja")
base.copy_file("v8/third_party/depot_tools/ninja-linux64", "v8/third_party/ninja/ninja")