Skip to content

Add cross-compilation support for tools and data builds - #1501

Open
ghostflyby wants to merge 2 commits into
BYVoid:masterfrom
ghostflyby:cross-compile-build-data
Open

ghostflyby wants to merge 2 commits into
BYVoid:masterfrom
ghostflyby:cross-compile-build-data

Conversation

@ghostflyby

@ghostflyby ghostflyby commented Sep 29, 2026 •

Copy link
Copy Markdown

Building the dictionaries runs the just-built opencc_dict and opencc binaries at build time (data/CMakeLists.txt), so configuring for a target architecture the host cannot execute — iOS, Android, Windows ARM64 on x64 — always failed mid-build (#615). The MSVC CI works around this today by compiling an explicit ARM64 target list that avoids the data targets.

This adds OPENCC_BUILD_TOOLS/OPENCC_BUILD_DATA options plus host-tool overrides, following the runnability idiom already established by the jieba plugin (OPENCC_CAN_RUN_CPPJIEBA_DICT):

  • Native builds are unchanged.
  • When the host cannot execute the target tools and no host tools are provided, OPENCC_BUILD_DATA defaults to OFF with a WARNING explaining the escape hatches; the build completes (library + tools) instead of failing. The automatic default is not written to the cache, so adding host tools later and reconfiguring picks them up instead of treating the first automatic OFF as user-provided; conversely, providing host tools while OPENCC_BUILD_DATA is explicitly OFF draws its own warning. -DOPENCC_BUILD_DATA=ON overrides for emulation setups such as Rosetta 2. Host-runnable cross configurations keep building data via the host/target architecture comparison, which upgrades the verdict only on a Windows host (WOW64 — x86 on x64, including the Visual Studio generator's -A platforms, which do not set CMAKE_CROSSCOMPILING); cross builds to Windows from non-Windows hosts (mingw triplets) stay unrunnable and auto-disable data. Setting CMAKE_CROSSCOMPILING_EMULATOR (e.g. qemu) also keeps data enabled: the build-time tool invocations — the dictionary and jieba merged-dict commands, and the converter passed to the ST phrase generator as repeated --opencc=<token> options — are prefixed with the emulator, which add_custom_command does not do on its own.
  • With host tools provided (OPENCC_DICT_EXECUTABLE + OPENCC_CLI_EXECUTABLE — the ST phrase generator also executes the opencc CLI), dictionaries are generated by the host tools and installed as usual; the tools are probed with opencc_dict --help at configure time (their cache doc strings note they must come from the same OpenCC source version), and on non-Windows hosts they run wrapped in LD_LIBRARY_PATH/DYLD_LIBRARY_PATH pointing at the lib dir of their installation prefix, shared by data/ and the jieba plugin. Dictionaries are architecture-independent data: host-generated and target-built files are byte-identical (shasum-verified).
  • The jieba plugin participates: integrated builds with host tools generate jieba_merged.ocd2 with the host opencc_dict even with OPENCC_BUILD_TOOLS=OFF. When data is not built (auto-OFF cross builds, or explicit OPENCC_BUILD_DATA=OFF), no *_jieba.json configs are installed either — they would point at dictionaries that do not exist.

Detailed Changes:

  • Build options: OPENCC_BUILD_TOOLS (default ON) gates src/tools; OPENCC_BUILD_DATA (default ON, auto-OFF as above, uncached) gates data/. Both are OPENCC_-prefixed like OPENCC_ENABLE_INSTALL so FetchContent / add_subdirectory builds cannot inherit a parent project's same-named cache values. ENABLE_GTEST now fails configuration when tools or data are disabled, since the conversion tests execute the opencc CLI against the built dictionaries. OPENCC_BUILD_DATA without OPENCC_BUILD_TOOLS or host tools is likewise a configure-time error, as are nonexistent or half-provided host-tool paths.
  • Shared runnability helpers: the jieba plugin's opencc_normalize_arch / opencc_can_run_target_on_host logic moved to cmake/OpenCCRunnability.cmake and is reused by the top level, together with a new opencc_host_tool_command helper that wraps host-built tools with the dynamic-library path env. The host/target architecture comparison sits outside the CMAKE_CROSSCOMPILING check so it also covers the Visual Studio generator, upgrades only on a Windows host (WOW64), and CMAKE_CROSSCOMPILING_EMULATOR forces the verdict back to runnable. The plugin keeps an inline fallback for standalone builds without the parent tree, and only fails when neither OPENCC_BUILD_TOOLS nor host tools can provide opencc_dict.
  • Windows and iOS fixes: copy_libopencc_to_dir_of_opencc_dict moved next to the tools in src/ (both data/ and plugins/jieba/ reference it) so it survives OPENCC_BUILD_DATA=OFF; tool executables install with a BUNDLE destination so MACOSX_BUNDLE (iOS) installs work.
  • ST phrase generator: --opencc is now repeatable (action="append"), and the CMake data build passes the converter command as repeated --opencc=<token> options so an emulator or env-wrapper prefix (whose tokens may start with -, e.g. cmake -E env) survives being passed as an argument; the Bazel genrule's single-path invocation is unchanged.
  • CI: the MSVC ARM64 job drops its explicit compile-only target list; data generation is now skipped automatically.
  • Docs: NEWS entry under 1.4.3.

Testing (macOS, arm64 host):

Configuration Result
Native default + ENABLE_GTEST=ON 21/21 tests pass, 26 dictionaries
iOS (CMAKE_SYSTEM_NAME=iOS) default WARNING, data auto-OFF, arm64 library + tools build
iOS + host tools 26 dictionaries, sha256-identical to the native build
iOS, host tools added in a second configure of the same build dir data auto-OFF does not persist; dictionaries are generated with the host tools
-DCMAKE_OSX_ARCHITECTURES=x86_64 (arm64 host) not CMAKE_CROSSCOMPILING; data built, tools run under Rosetta 2
-DCMAKE_CROSSCOMPILING_EMULATOR=... (iOS) data stays ON; build-tree tool invocations prefixed with the emulator (configure-verified)
Native + OPENCC_BUILD_TOOLS=OFF + host tools + jieba plugin configures and generates jieba_merged.ocd2 via the host tool (configure-verified)
-DOPENCC_BUILD_DATA=OFF library only
-DOPENCC_BUILD_TOOLS=OFF, or -DENABLE_GTEST=ON -DOPENCC_BUILD_DATA=OFF configure-time FATAL_ERROR, as intended
Nonexistent / half-provided host-tool paths configure-time FATAL_ERROR, as intended

Bazel and Node builds are untouched; dictionary files and test cases are unchanged, so the CONTRIBUTING dictionary checklist does not apply. The CMake test suite passes (21/21).

Example — shipping full dictionaries for a cross target via vcpkg's host-dependency mechanism:

{ "name": "opencc", "host": true, "features": ["tools"] }
"-DOPENCC_DICT_EXECUTABLE=${CURRENT_HOST_INSTALLED_DIR}/tools/opencc/opencc_dict"
"-DOPENCC_CLI_EXECUTABLE=${CURRENT_HOST_INSTALLED_DIR}/tools/opencc/opencc"

Implements the build flag requested in #615, extended with host-tool support.

@ghostflyby
ghostflyby force-pushed the cross-compile-build-data branch from 3a7ea8c to 8fad261 Compare September 30, 2026 02:07

@frankslin frankslin left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for picking up #615 — the overall direction (host-tool overrides, copy_libopencc_to_dir_of_opencc_dict moving into src/, BUNDLE DESTINATION, failing ENABLE_GTEST early) looks good. A few issues before this can go in, the first one being a CI failure.

1. MSVC ARM64 job fails (regression in the shared runnability logic)

build-and-test (amd64_arm64, ARM64, arm64, false) fails in the very step this PR changes:

Building CJK_Compatibility_Ideographs.ocd2
This version of ...\opencc_dict.exe is not compatible with the version of Windows you're running.
error MSB8066 ...
Generating Release/jieba_dict/jieba_merged.ocd2   <- also fails

and the "BUILD_DATA defaulted to OFF" warning never appears at configure time. With the Visual Studio generator, -A ARM64 on an x64 host does not set CMAKE_CROSSCOMPILING (no CMAKE_SYSTEM_NAME). The original jieba code ran the WIN32 host/target arch comparison outside the CMAKE_CROSSCOMPILING check; the extracted cmake/OpenCCRunnability.cmake nests it inside, so OPENCC_CAN_RUN_TARGET_TOOLS is always TRUE here. This also regresses the jieba plugin, which used to skip jieba_merged.ocd2 correctly in this job. See inline comments.

2. Host tools are not used for the jieba merged dictionary

  • Integrated build, cross compiling (e.g. iOS) with OPENCC_DICT_EXECUTABLE/OPENCC_CLI_EXECUTABLE and BUILD_OPENCC_JIEBA_PLUGIN=ON: OPENCC_CAN_RUN_CPPJIEBA_DICT is FALSE, so jieba_merged.ocd2 is silently not built while the plugin and *_jieba.json configs are still installed — they break at runtime.
  • Native build with BUILD_TOOLS=OFF + host tools + jieba: hits the new FATAL_ERROR whose message suggests "or provide host tools", even though host tools are provided.

In integrated mode, when OPENCC_USING_HOST_TOOLS is set, the plugin should use OPENCC_DICT_EXECUTABLE as OPENCC_DICT_COMMAND (and not require BUILD_TOOLS).

3. Auto-OFF BUILD_DATA gets stuck in the cache

On the first configure without host tools, option() caches BUILD_DATA=OFF. If the user then re-configures adding -DOPENCC_DICT_EXECUTABLE=... -DOPENCC_CLI_EXECUTABLE=..., BUILD_DATA stays OFF (the cache entry now counts as "provided"), the host tools are silently ignored, and the warning no longer prints. Consider not caching the auto-derived default, or at least warning when host tools are set while BUILD_DATA is OFF.

4. Unprefixed option names

BUILD_TOOLS / BUILD_DATA are very common names. When OpenCC is consumed via FetchContent / add_subdirectory and the parent project already has BUILD_TOOLS=OFF in its cache, OpenCC inherits it, BUILD_DATA defaults to ON, and configuration hits FATAL_ERROR. Suggest OPENCC_BUILD_TOOLS / OPENCC_BUILD_DATA, in line with OPENCC_ENABLE_INSTALL.

5. Smaller points

  • Host tools are only checked for existence. A host tool from a different OpenCC version may generate inconsistent dictionaries or fail mid-build; the standalone jieba build already probes opencc_dict --help for capability — something similar (or at least documenting that host tools must match the source version) would help.
  • On non-Windows, data/ runs host tools directly; a dynamically linked host tool installed in a non-default prefix won't find libopencc. The standalone jieba path already handles this with LD_LIBRARY_PATH/DYLD_LIBRARY_PATH.
  • BUILD_DATA=OFF also skips installing data/config/*.json, but the warning only mentions dictionaries. With jieba on, *_jieba.json are still installed and point to dictionaries that don't exist.
  • Optional: don't auto-disable data when CMAKE_CROSSCOMPILING_EMULATOR is set — CMake prepends the emulator for executable targets used as COMMAND (note --opencc $<TARGET_FILE:opencc> is passed as an argument, so it would need separate handling).

opencc_normalize_arch(OPENCC_TARGET_ARCH "${_OPENCC_TARGET_ARCH_RAW}")
opencc_normalize_arch(OPENCC_HOST_ARCH "${CMAKE_HOST_SYSTEM_PROCESSOR}")

set(OPENCC_CAN_RUN_TARGET_TOOLS TRUE)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The WIN32 host/target arch comparison was outside the CMAKE_CROSSCOMPILING check in the original jieba code. VS generator + -A ARM64 on x64 leaves CMAKE_CROSSCOMPILING FALSE, so with this nesting OPENCC_CAN_RUN_TARGET_TOOLS stays TRUE and the ARM64 opencc_dict.exe is executed on the x64 runner (this is the MSVC ARM64 CI failure). Please move the WIN32 comparison out of this if to restore the previous semantics.

Comment thread CMakeLists.txt Outdated
set(_OPENCC_BUILD_DATA_DEFAULT ON)
set(_OPENCC_BUILD_DATA_AUTO_OFF OFF)
if(NOT _OPENCC_BUILD_DATA_PROVIDED
AND CMAKE_CROSSCOMPILING AND NOT OPENCC_CAN_RUN_TARGET_TOOLS

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requiring CMAKE_CROSSCOMPILING here means the Windows -A ARM64 case never auto-disables data either. Once OPENCC_CAN_RUN_TARGET_TOOLS is fixed, this can probably just check NOT OPENCC_CAN_RUN_TARGET_TOOLS.

Comment thread CMakeLists.txt Outdated
Comment thread plugins/jieba/CMakeLists.txt Outdated
NOT BUILD_TOOLS)
message(FATAL_ERROR
"Building the merged Jieba dictionary requires the opencc_dict build "
"tool: set BUILD_TOOLS=ON or provide host tools.")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"or provide host tools" doesn't help here: in integrated mode the plugin always uses the build-tree opencc_dict target and ignores OPENCC_DICT_EXECUTABLE. Likewise when cross compiling with host tools, OPENCC_CAN_RUN_CPPJIEBA_DICT is FALSE and the merged dictionary is silently skipped. When OPENCC_USING_HOST_TOOLS is set, the integrated branch should use OPENCC_DICT_EXECUTABLE instead.

@ghostflyby
ghostflyby force-pushed the cross-compile-build-data branch from ac2f4d7 to 7ea2fb7 Compare October 4, 2026 08:16
Building the dictionaries executes the just-built opencc_dict and opencc
tools at build time, so configuring for a target architecture the host
cannot execute (iOS, Android, Windows ARM64 on x64, ...) always failed
mid-build. Add BUILD_TOOLS/BUILD_DATA options and host-tool overrides so
cross builds either skip data generation automatically or generate the
architecture-independent dictionaries with host-built tools, as proposed
in BYVoid#615.

Detailed Changes:

**Build options**

- BUILD_TOOLS (default ON) gates src/tools; BUILD_DATA (default ON) gates
  data/. Cross compiling to an architecture the host cannot execute now
  defaults BUILD_DATA to OFF with a warning instead of failing later in
  the build; host-runnable cross configurations (x86 on x64 Windows) keep
  building data, and emulation setups (Rosetta 2) can force -DBUILD_DATA=ON.
- ENABLE_GTEST now fails configuration when tools or data are disabled,
  since the conversion tests execute the opencc CLI against the built
  dictionaries.

**Host tools**

- OPENCC_DICT_EXECUTABLE and OPENCC_CLI_EXECUTABLE accept host-built tool
  paths; dictionaries generated with host tools are byte-identical to
  target-built ones.

**Shared runnability helpers**

- The jieba plugin's architecture normalization and can-run-on-host logic
  moved to cmake/OpenCCRunnability.cmake and is reused by the top level;
  the plugin keeps an inline fallback for standalone builds without the
  parent source tree.

**Windows and iOS build fixes**

- copy_libopencc_to_dir_of_opencc_dict moved next to the tools (data/ and
  jieba/ both use it) so it survives BUILD_DATA=OFF; tool executables now
  install with a BUNDLE destination so MACOSX_BUNDLE (iOS) installs work.
- The MSVC ARM64 job no longer needs an explicit compile-only target list.
(BYVoid#1) 可运行性判断嵌套回归(MSVC ARM64 CI 失败)+ 复审 blocking:提升分支 gate CMAKE_HOST_WIN32: WIN32 宿主/目标架构对比为平级独立 if(在 CMAKE_CROSSCOMPILING 检查外,附注释)。按复审 blocking 修正提升方向:不可运行恒降 FALSE;可运行仅在 CMAKE_HOST_WIN32 时回 TRUE(架构对比的 WOW64 语义只对 Windows 宿主成立,非 Windows 宿主交叉到 Windows(mingw triplet)即使架构相同也维持 FA
(BYVoid#2) 自动关 DATA 的条件不应叠加 CMAKE_CROSSCOMPILING: 顶层默认值判断条件仅 NOT OPENCC_CAN_RUN_TARGET_TOOLS 且未提供宿主工具;CMAKE_CROSSCOMPILING 不再出现于条件(复审判定放行,未改动)。
(BYVoid#3) jieba 集成模式接入宿主工具: 集成模式 + OPENCC_USING_HOST_TOOLS 时 OPENCC_CAN_RUN_CPPJIEBA_DICT 置 TRUE,OPENCC_DICT_COMMAND 用宿主 OPENCC_DICT_EXECUTABLE 经 opencc_host_tool_command 包裹(非 WIN32 走 cmake -E env LD/DYLD_LIBRARY_PATH,前缀取工具上两级),
(BYVoid#4) 自动 OFF 不应写入 cache: get_property(... CACHE OPENCC_BUILD_DATA PROPERTY TYPE SET) 探测;未提供设普通变量每次重算、完全不落 cache;提供则尊重 cache;宿主工具已设置但显式 OFF 时输出警告(复审判定放行,未改动)。
(BYVoid#5) 选项加 OPENCC_ 前缀: 全局改名覆盖所有 CMake 文件、注释、错误文案、NEWS.md 与 PR 草稿(复审判定放行,未改动)。
(BYVoid#6) 宿主工具能力探测与版本说明: 存在性检查后以(env 包裹后的)命令执行 opencc_dict --help,非零退出 FATAL;两个 cache 文档字符串注明须与同一份 OpenCC 源码版本同源(复审判定达标,未改动;复审次要观察的 CLI 探测为可选增强,未做)。
(BYVoid#7) 非 Windows 宿主工具的动态库路径: opencc_host_tool_env(仅 cmake -E env + LD/DYLD_LIBRARY_PATH 前缀,Windows 为空)+ opencc_host_tool_command(前缀+工具)两个共享辅助函数;data/ 直接命令与 jieba 集成/standalone 宿主工具命令复用;ST 生成器例外:env 前缀包住整个 Python 调用(spawn 的 CLI 继承
(BYVoid#8) 警告涵盖 configs;DATA=OFF 时不安装指向缺失字典的 jieba 配置: 警告文案同时提及 dictionaries 与 config files(逐字包含 OPENCC_BUILD_DATA defaulted to OFF);*_jieba.json 安装门控为 CAN_RUN 且(standalone 或 OPENCC_BUILD_DATA);NEWS 说明行为(复审判定放行,未改动)。
(BYVoid#9) CMAKE_CROSSCOMPILING_EMULATOR 不自动关 data + 复审 blocking:jieba merged 字典也加 emulator 前缀: 模块末尾 if(CMAKE_CROSSCOMPILING_EMULATOR) 置 TRUE(fallback 同步)。data/:直接命令冠 ${CMAKE_CROSSCOMPILING_EMULATOR} 前缀,转换器以重复 --opencc=<token> 选项(emulator 与目标路径各一项,foreach 构造)传给 ST 生成器,脚本 --opencc 为 action=append

(follow-up P1) 宿主工具动态库环境误用目标平台变量:opencc_host_tool_env 及 jieba 内联回退改用 CMAKE_HOST_WIN32/CMAKE_HOST_APPLE —— 工具在宿主执行,macOS 交叉编译到 Linux 目标时也应设置 DYLD_LIBRARY_PATH(单元级复现验证:模拟 Linux 目标 + macOS 宿主,修复前给 LD、修复后给 DYLD)。
(follow-up P2) OPENCC_BUILD_DATA 显式探测只查 cache、漏普通变量:补 DEFINED 普通 变量判断(CMP0077=NEW 下父项目 add_subdirectory 前的 set 不再被自动默认值覆写;单元级验证:父项目同时设 DATA/TOOLS=OFF 经 add_subdirectory 消费,配置成功无 FATAL)。自动默认不落 cache 的行为保持不变。全部 13 个构建验证门回归通过。
@ghostflyby
ghostflyby force-pushed the cross-compile-build-data branch from 7ea2fb7 to ca5da29 Compare October 4, 2026 15:38
@ghostflyby
ghostflyby requested a review from frankslin October 5, 2026 15:15

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants