在 WSL2 中编译 ONLYOFFICE Core 模块的踩坑记录

编译日期:2026-09-24
编译目标:ONLYOFFICE Core 模块(python3 ./automate.py core --update=0)


环境信息

硬件

项目

信息

CPU 核心

24

内存

16 GB

平台

x86_64

操作系统

项目

信息

宿主机

Windows 10 (DESKTOP-90GKU0J)

WSL

WSL2 (Ubuntu 20.04.6 LTS Focal)

内核

6.18.33.2-microsoft-standard-WSL2 SMP PREEMPT_DYNAMIC

编译工具链

工具

版本

说明

GCC/G++

9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.2)

系统 gcc

Python 3

3.8.10

系统自带

Git

2.25.1

系统自带

CMake

3.16.3 → 升级至 3.28+

需升级,见坑十

Ninja

未安装(使用 v8 自带的 1.8.2)

OpenJDK

11.0.27

需设置 JAVA_HOME

ld.gold

2.34 (v1.16)

系统自带

ld.bfd

2.34

系统自带

libstdc++.so.6

6.0.28 (GLIBCXX_3.4.28)

系统自带

仓库版本

仓库

版本/标签

提交

build_tools

v9.4.0.124

cf4cac0 Merge pull request '#198' (release/v9.4.0)

core

v9.4.0.131

55e5f973b0 Fix UTF-8 BOM position

v8

branch-heads/8.9

a2d8eca22ad Version 8.9.255.25

depot_tools

latest

91b9ca4db Roll recipe dependencies (trivial)

libheif

1.18.2

—

libde265

1.0.16

—

x265

4.1

—

sysroot 信息

项目

信息

路径

build_tools/tools/linux/sysroot/ubuntu16-amd64-sysroot

ld.gold

2.26.1 (v1.11) — 不支持 = 前缀语法

ld.bfd

2.26.1 — 不支持 --threads

libstdc++.so.6

6.0.21 (GLIBCXX_3.4.21) — 版本过低


坑一:中断后无法重新触发 v8 初始化

现象

中断了 core/Common/3dParty 的初始化后,重新运行 automate.py core,发现 v8 相关目录已经存在但状态不完整,脚本不会重新初始化。

原因

v8_89.py 的 make() 函数通过以下方式判断是否跳过初始化:

  1. v8.data 文件 — 内容为 v8_version_1,作为版本标记。存在则认为已初始化。

  2. 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),通过(cd"(cd "(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.bfd 替换 ld.gold

ld.bfd 不认 --threads 选项(编译命令里有 -Wl,--threads)

用系统的 ld.bfd 2.34 替换

同样不认 --threads

用系统的 ld.gold 2.34 替换

2.34 的 gold 也不认 = 前缀

最终解决

保留 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

链接器

--threads

= 前缀

去掉 = 后

sysroot ld.gold 2.26.1

✅

❌

✅

sysroot ld.bfd 2.26.1

❌

✅

✅

系统 ld.gold 2.34

✅

❌

✅

系统 ld.bfd 2.34

❌

✅

✅


坑九: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+)。

来源

libstdc++ 版本

最高 GLIBCXX

sysroot

6.0.21

3.4.21

系统

6.0.28

3.4.28

解决

将 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")


在 WSL2 中编译 ONLYOFFICE Core 模块的踩坑记录
https://blog.cikaros.cn/archives/wei-ming-ming-wen-zhang-bSOJ3DKt
作者
Cikaros
发布于
2026年09月24日
许可协议