欢迎加入开源鸿蒙PC社区: https://harmonypc.csdn.net/

欢迎在PC社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper

项值
对象cmake 3.31.12(跨平台构建系统生成器,BSD-3-Clause)
环境鸿蒙 PC:HUAWEI MateBook Pro(HAD-W32)/ HarmonyOS 6.1.0 / aarch64(全部实测,见 2.1)
仓库build_in_harmonyos(AtomGit)
PR#18259(2026-11-03 已合入 main)

摘要:本文是构建工具 cmake 3.31.12 在鸿蒙 PC(aarch64,HMDFS 文件系统 + bionic 用户态)上的适配全记录。面对"CMake 不认识 HarmonyOS"与"bionic 缺 glibc 专属符号"两个平台缺口,适配采用零源码补丁路线:配方层显式声明目标为 Linux(aarch64)并注入 __OHOS__ 守卫的 bionic weak 符号兼容 shim,测试层用 LD_PRELOAD 宿主模拟 + 干净构建树。量化结果:上游 ctest 全量套件(644 项注册)四轮演进 344→564→601→621,终口径 621/644 = 96.4%(≥路径 C 门禁 90%);23 项失败中 21 项为平台条件(8 族逐项归因)+ 2 项待查,0 适配缺陷;鸿蒙特有消费者测试 5/5 全部通过(test_package 四件套 4/4 + 消费者用例 1/1);产物 NEEDED 仅 libc.so(静态自包含)。PR #18259 已合入 main(2026-11-03),4 套知识草稿随 PR 入仓待转正。

CMake 根本不认识 HarmonyOS——内核自报的 “HarmonyOS” 被它判为未知系统,整条构建链被引到 Windows 实现上;bionic 用户态又缺着 cmake 依赖的 glibc 符号。本文记录的,是如何在"零源码补丁"约束下,从首败走到 96.4% 的上游测试通过率并合入 main。

声明:本文 23 项上游失败测试全部归因为平台条件或待查(逐项归因见 4.1),不是适配缺陷,但本文不宣称"100% 通过";结论限定于当前鸿蒙 PC(HMDFS+bionic)环境与 cmake 3.31.12 版本。本文范围止于 PR 合入,ohpcd 制品发布与下游消费为后续步骤,不在本文展开。

目录:1 背景 · 2 环境与差异前置 · 3 适配过程(5 坑) · 4 验证(4.1-4.7 分层) · 5 知识沉淀 · 6 FAQ · 7 总结 · 参考链接

1. 背景:为什么值得做

生态价值(第一层):cmake 是构建工具链的地基——本生态绝大多数 C/C++ 库的 Conan 配方都 tool_requires 一个 cmake。而适配本包之前,制品仓(ohpcd)里的 cmake 配方数为 0,整个生态的 cmake 可用性全押在 CI worker 镜像的均质性上:哪台机器自带能跑的 cmake,构建就在哪台机器过。

真实先例(第二层):CI worker(OHOS 机器)上的 HNP cmake 动态链接缺 libc++、无法加载(Error relocating …std::__n1…: symbol not found / Error 127),已 2 轮 CI 实证(赛事任务记录口径,构建编号见任务记录,本文不引用)。下游 clickhouse/26.7.6.57-stable 的 CI 转绿,硬性依赖本包先发布。

选库理由(第三层):构建工具类"零第三方依赖"可行(13 个可选 utility 库全部走 in-tree 副本)、静态自包含可行(产物 NEEDED 只留 libc.so)、且它的可用性是所有下游包的硬前置——一包解锁整条生态。版本选 3.31.12(3.31.x 线最新,2026-04-22 官方);禁选 4.x:CMake 4.0 起硬拒 cmake_minimum_required < 3.5,而生态语料里有老 minimum 声明。

2. 环境与差异前置

2.1 环境表(2026-10-22 适配 / 2026-11-08 复核)

项值测量方式
设备HUAWEI MateBook Pro(HAD-W32)系统设置页
系统HarmonyOS 6.1.0(用户可见);内部版本 206.1.0.070 (X.26050603.GENERIC_GENERIC_ARM_64ONLY_PC_CHINA_FULL_DPI480_EXT4)系统设置页 / cat /etc/component_version.txt
架构aarch64uname -m
内核HarmonyOS HongMeng Kernel 1.12.0uname -r
工具链HNP clang/clang++ 15.0.4(Target: aarch64-unknown-linux-ohos,/data/service/hnp/bin)/data/service/hnp/bin/clang --version
构建工具Conan 2.29.1(社区适配 os=OHOS)conan --version
Profileohos-aarch64:os=OHOS / arch=armv8 / compiler.libcxx=libc++ / compiler.cppstd=17 / build_type=Releaseci/conan/profiles/ohos-aarch64 原文
SDKOHOS SDK 26.0.0.18profile 硬编码路径 /data/service/hnp/ohos-sdk.org/ohos-sdk_26.0.0.18/ohos/native
外层构建器cmake 4.1.2(HNP,构建 3.31.12 源码本身用)本机实测(2026-10-22)
上游源(规范源)https://github.com/Kitware/CMake/releases/download/v3.31.12/cmake-3.31.12.tar.gz真机 curl -sI -L HEAD 200(2026-11-08 实测,无需镜像)
SHA-2565f3fd5a54dfa65602bdbed64f981a72673cc19f2d304cc2955cf0dfa0cfd8272与 Kitware 官方 cmake-3.31.12-SHA-256.txt 逐项交叉校验
构建规模上游 tarball 11715065 B(≈11.2 MB,ls 实测);上游全量测试 644 项,单轮 2889.64 s(≈48 分 10 秒,run10 实测)ls -la / ctest 日志

2.2 差异点总表(先列全,第 3 节逐个展开)

#差异点上游假设鸿蒙 PC 实际情况对应坑位
1宿主身份uname(2) 返回已知 Linux 发行版名内核自报 “HarmonyOS” → CMake 判 unknown system → UNIX=OFF → 分支误选 Windows 实现坑 1
2用户态 C 库glibc(pthread affinity 符号、UAPI 头语义)bionic:符号缺失 + linux/socket.h UAPI 结构体重定义坑 2
3内核 release 格式数字(X.Y.Z)“HongMeng Kernel 1.12.0” 字母开头 → 每次 cmake/ctest 启动向 stderr 写一行 /proc/meminfo 解析告警坑 3
4文件系统语义标准 POSIX(chmod/hardlink/FIFO/AF_UNIX/rename)HMDFS:chmod 被忽略、特殊文件操作 EPERM、rename 瞬时竞态坑 4
5系统工具ldconfig/file(1)/OpenSSL 开发头在场bionic/HNP 缺失;file(1) 把 aarch64 PIE 误标为 “ELF shared object”坑 4
6CI 门禁形态门禁静态扫描识别 self.run 形态原 test() 用 subprocess.run → 被判"空壳测试"坑 5

3. 适配过程

3.0 侦察

依赖发布状态:零第三方依赖——13 个可选 utility 库(cmcurl/cmexpat/cmlibarchive/cmliblzma/cmlibuv/cmjsoncpp/cmrhash/cmnghttp2/cmbzip2/cmform/cmzlib/cmzstd 等)全部使用上游自带 in-tree 副本,配方无任何 requires;无依赖需要进制品仓核验。唯一要盯的默认值:CMake 在 Linux 平台默认 CMAKE_USE_OPENSSL=ON,配方必须显式置 OFF(否则 conan 会去找 openssl 依赖)。

构建系统:纯上游 CMake 工程(自举友好),C++17,无汇编、无闭源二进制、无 patch。外层构建用 HNP cmake 4.1.2 构建 3.31.12 源码(鸡生蛋:仓里没有别的 cmake 可用,这是标准路径不是 hack)。

测试策略:双轨——探索构建 BUILD_TESTING=ON(全量 ctest,用于平台取证);发布配方 BUILD_TESTING=OFF(制品最小化,测试证据由探索轨承载)。

依赖树:

cmake/3.31.12(配方,零 requires)
├── in-tree 副本(共 13 个,不对外链接):cmcurl / cmexpat / cmlibarchive / cmliblzma /
│   cmlibuv / cmjsoncpp / cmrhash / cmnghttp2 / cmbzip2 / cmform / cmzlib / cmzstd 等
├── 系统库:libc(bionic)——链接后唯一的 NEEDED
└── 测试层(不进制品):LD_PRELOAD libuname_linux.so
    (自写宿主模拟拦截库,dlsym(RTLD_NEXT) 链真 uname(),仅测试/探索构建用)

3.1 坑 1:CMake 不认识 HarmonyOS → UNIX=OFF → 误选 Windows 实现

现象:探索构建首败(BUILD_RC=2):

Source/kwsys/ProcessWin32.c: fatal error: 'windows.h' file not found

Unix 侧的构建任务里出现了 Windows 实现文件——这个报错本身就是线索。

定位:查 CMake 的平台发现机制:它通过 uname(2) 读宿主系统名,在 Platform/ 目录匹配平台模块。本机 uname -s 返回 HarmonyOS——CMake 的 Platform/ 里没有 HarmonyOS.cmake(对 v3.31.12 与 master 两个 ref 分别核验,均 404)→ 系统 “unknown to cmake” → UNIX 被置为 OFF → kwsys 的 CMakeLists 以 if(NOT UNIX) 选择 Process 实现 → 选到了 ProcessWin32.c。整条因果链:内核自报名 → 平台匹配失败 → UNIX=OFF → Windows 实现。

方案:构建侧显式声明目标环境:

-DCMAKE_SYSTEM_NAME=Linux -DCMAKE_SYSTEM_PROCESSOR=aarch64

(命令行 -D 传入;Conan 工具链模板对这两个 set 有 if(NOT DEFINED …) 守卫,命令行值优先,工具链文件零修改。)

误判→纠正:最早我试的是把 PATH 前置一个假的 /bin/uname 程序 shim——实证无效。原因:CMake 的宿主检测走的是 libc 的 uname(&uts_name) 函数调用(cmSystemTools.cxx:3760),根本不调 /bin/uname 程序。纠正为 LD_PRELOAD 拦截库:在 uname 符号上挂钩,改 sysname 后 dlsym(RTLD_NEXT) 链回真实现——测试/探索构建专用,不进配方、不进制品。

派生子坑(旧树残留):修好宿主声明后在旧构建树上重配置,“System is unknown” 告警照旧。定位:CMake 把宿主状态持久化在 CMakeFiles/<version>/CMakeSystem.cmake,重配置时以持久化文件为准(cmGlobalGenerator.cxx:638-662)——宿主身份是构建期的快照,不是每次启动的实时值。连带问题:旧树编译出的测试可执行文件缺 build RUNPATH(readelf -d 对照:旧树 0 条 vs 干净树 1 条)。处置:一切结论以干净构建树为准(LD_PRELOAD 贯穿 configure+build 全程重建)。

经验:构建系统的"宿主身份"是构建期快照;平台判定异常时先查持久化状态文件再怀疑实时检测;"宿主模拟"必须模拟到内核接口层(函数),不是命令行工具层。

入档:本案已入档知识草稿 DRAFT-ohos-cmake-unknown-system-unix-off(错误签名 System is unknown to cmake,含 PATH-shim 无效对照组结论),条目见第 5 节。

3.2 坑 2:bionic 缺 glibc 符号 + UAPI 头冲突(双错误同源)

现象:修复坑 1 后,keep-going 全量收集错误,剩余两类编译/链接错误:

Source/cmAffinity.cxx:53: use of undeclared identifier 'pthread_getaffinity_np'
ld.lld: error: undefined symbol: pthread_setaffinity_np
        referenced by process.c.o:(uv__process_child_init) in archive libcmlibuv.a
sysroot/usr/include/linux/socket.h:23:8: error: redefinition of 'sockaddr_storage'

定位:两个错误、一个根因——bionic 不是 glibc:

  • pthread_getaffinity_np:bionic 的 <pthread.h> 没有 affinity 原型(在 sysroot 26.0.0.18 的 pthread.h 中 grep 实证无匹配),库中也没有符号;<sched.h> 的 cpu_set_t/sched_* 需要 glibc 式特性测试门控(_GNU_SOURCE)。上游在 #elif defined(__linux__)(aarch64-unknown-linux-ohos 目标为真)下无条件使用 pthread 变体;in-tree cmlibuv 的 _GNU_SOURCE 预期 glibc 声明,在 bionic 下退化为 C 隐式声明(仅警告)——编译能过,链接必挂。
  • sockaddr_storage:bionic 的 <sys/socket.h> 只在 #ifdef __MUSL__ 时 include UAPI 的 <linux/socket.h>(否则自定义该结构体);而 cmcurl 的 check_include_file("linux/tcp.h") 在 bionic 上命中 → 定义 HAVE_LINUX_TCP_H → <linux/tcp.h> 无条件 include <linux/socket.h> → UAPI 版 sockaddr_storage 二次定义。glibc 下无此冲突。

方案:-include 注入兼容头(配方 generate() 生成,__OHOS__ 守卫,对非 OHOS 目标零影响),核心代码:

// cmake_ohos_compat.h(__OHOS__ 守卫)
#include <sched.h>
__attribute__((weak)) int pthread_getaffinity_np(pthread_t thread,
                                                 size_t cpusetsize, cpu_set_t *cpuset) {
  (void)thread;                       /* bionic 无 thread-affinity,pid=0 = 调用线程 */
  return sched_getaffinity(0, cpusetsize, cpuset);
}
/* pthread_setaffinity_np 同理转调 sched_setaffinity(0, …) */

weak 符号链接期覆盖 in-tree cmlibuv 的引用;链接命令无 --gc-sections(实测验证),weak 符号不会被丢弃。UAPI 冲突则用 cache 预置消解:-DHAVE_LINUX_TCP_H=0 使 check 跳过,curl 走自身 #elif HAVE_NETINET_TCP_H 可移植路径(curl 自己的 WindowsCache.cmake 对非 Linux 平台同样置 0,属上游支持配置)。零源码补丁。

已弃方案:#define 宏把 pthread_* 映射到 sched_*——pthread_t 与 pid_t 类型不一致,宏改写上流声明产生 conflicting types 错误。宏是声明级工具,做不了类型级转换。

经验:交叉目标叫 linux-ohos 但用户态是 bionic——“目标三元组里有 linux"≠"glibc 语义在场”;glibc 专属符号的缺口用 weak 符号 shim(链接层)补,头文件冲突用上游支持的 cache 变量绕(配置层),两条路都不需要碰源码。

入档:本案已入档知识草稿 DRAFT-ohos-bionic-glibc-gap-compat-shim(错误签名 use of undeclared identifier 'pthread_getaffinity_np' / redefinition of 'sockaddr_storage',含 #define 宏方案弃用原因),条目见第 5 节。

3.3 坑 3:内核 release 字母开头 → /proc/meminfo 告警污染 stderr(20 项失败的同一机制)

现象:宿主模拟修复 UNIX=OFF 后(通过率 53.4%→87.6%→93.3% 的演进中,见 4.1),仍有一族 20 项测试同签名失败:它们都是严格 expected-stderr 断言(期望 stderr 为空),而每次嵌套启动 cmake/ctest 都会向 stderr 写一行:

Problem parsing /proc/meminfo

定位(机制链,全部源码级实证):

  1. uname -r 返回 “HongMeng Kernel 1.12.0”——字母开头;
  2. 只有宿主被识别为 Linux 时 CMake 才查询 Linux 内存信息(隔离实验实证:无宿主模拟时 trivial configure 无此告警,有模拟时每次嵌套 configure 必现);
  3. kwsys 的 QueryLinuxMemory(SystemInformation.cxx:4126-4144)只取 release 字符串的 release[0] 与 release[2] 两个单数字符作内核主/次版本——“HongMeng…” 下取到的是字母,linuxMajor=0;
  4. 分支 linuxMajor>=3 || (linuxMajor>=2 && linuxMinor>=6) 为假 → 落入 Linux <2.6 的旧格式解析分支;
  5. fscanf(fd, "Mem: %lu %lu …") 对照本机 /proc/meminfo 实际首行 MemTotal: 32680656 kB 解析失败;
  6. 代码走到 std::cerr << "Problem parsing /proc/meminfo\n"(4214 行);
  7. 对照实验:本机 /proc/meminfo 六个字段(MemTotal/MemFree/Buffers/Cached/SwapTotal/SwapFree)全部标准格式,release 若数字开头可完整解析 6/6——这不是 /proc 数据问题,是纯宿主身份派生物。

方案(测试层):宿主模拟库从"单维度"升级为"双维度"——除了 sysname→"Linux",同时把 release 改写为标准 Linux 数字格式 5.10.0。回归论证:全树 grep 无任何测试断言 release 字符串或 CMAKE_HOST_SYSTEM_VERSION,release 只被 QueryLinuxMemory 与宿主版本 cache 变量消费,后者无测试读取——改写无副作用。

效果:M 族 20/20 全部恢复(93.3%→96.4%,零回归,见 4.1)。

经验:严格 stderr 断言类测试是"每次启动写一行"型告警的放大器——一行无害告警 × 多次嵌套启动 = 20 项失败。这类失败先看共同签名再谈逐项修,归族后一条模拟全解。

入档:本案已入档知识草稿 DRAFT-ohos-cmake-uname-release-meminfo(错误签名 Problem parsing /proc/meminfo,含 7 步机制链与 release 双维度模拟的回归论证),条目见第 5 节。

3.4 坑 4:HMDFS 平台条件族(21 项稳定失败的归因方法论)

现象:干净构建树 + 完整宿主模拟后,仍 23 项失败。其中 21 项稳定复现、跨轮次不变化,是平台条件而非适配问题——需要逐项证明"不是我的锅"。

定位(8 族归因,逐项记录首错 + 机制 + 恢复条件):

族项数机制恢复条件
M20宿主身份派生:release 字母开头 → meminfo 旧格式分支 → stderr 告警(坑 3)宿主模拟双维度(已解)
P7权限位语义:HMDFS 忽略 chmod(期望 stat=400 实际 100660)、access(X_OK) 不可靠(既有知识 E524)文件系统支持 POSIX 权限位
S8特殊文件操作:AF_UNIX bind EPERM×3、make jobserver mkfifo EPERM×3、hardlink(2) EPERM×1、file(RENAME) 瞬时 EPERM×1(同目录手工 mv 复现成功 → 瞬时元数据竞态,非权限)HMDFS 开放对应特殊文件操作
T3缺系统工具:bionic/HNP 无 ldconfig(command -v ldconfig 为空)→ install 的 RUNTIME_DEPENDENCIES 步骤失败工具链补 ldconfig
L1加载器消息格式:bionic “Error loading shared library …: (needed by …)” ≠ glibc “error while loading shared libraries: …”,测试按 glibc 格式断言测试兼容双格式(上游问题)
O1缺 OpenSSL 开发包:BootstrapTest stage-2 自举走 Linux 默认 CMAKE_USE_OPENSSL=ON,本机仅运行时 libssl.so.3(配方侧显式 OFF,与此无关;且系旧树派生——旧缓存掩盖过它)补 OpenSSL 开发头
F1file(1) 输出语义:本机 file(1) 把 aarch64 PIE 可执行文件标为 “ELF shared object”(无 “executable” 关键词)→ is_file_executable=0 → fixup_bundle 判"不是有效 bundle"file(1) 对 PIE 输出 executable
A2待查:FileAPI codemodel installers 计数差异(基础 codemodel-v2 PASSED)、get_filename_component(PROGRAM) 对 “/” 返回空(EXISTS “/”=NO 复现)跟踪上游 Linux 环境对照

方案(方法论 = attribute-and-exclude):ledger 逐项归因(每项首错原文 + 机制 + 恢复条件),用 ctest --exclude-regex 维护平台条件名单跑排除口径,不改上游任何测试期望值、不删用例。21 项每项都有独立实证(chmod 前后 stat 对比、mkfifo 系统调用返回 EPERM、手工 mv 对照复现等,证据留本地)。

经验:“稳定失败"不等于"适配缺陷”——归因必须落到机制(文件系统语义/系统工具/消息格式),每一项都要有恢复条件;改上游测试期望值是掩盖,不是修复。

入档:本案已入档知识草稿 DRAFT-ohos-hmdfs-platform-conditions(错误签名 Operation not permitted / Could not find ldconfig / failed to create link / Error loading shared library.*needed by,含 8 族归因表与 rename 瞬时竞态对照组),条目见第 5 节。

3.5 坑 5(流程坑):CI 门禁静态扫描误判"空壳测试"

现象:conan create 已 RC=0,首次 finish 的 W3 本地 CI 模拟 FAIL——L0 门禁 stub_test_method 判 test_package 是空壳测试;同时 G1 配方完整性检查 FAIL。

定位:① 门禁对 test_package/conanfile.py 做的是 self.run 静态 grep,原 test() 用 subprocess.run 执行断言——行为是真实的,但门禁不认识这个形态;② G1 要求配方声明 export_sources() 与补丁应用调用(与 ctest/1.1.0 同类配方的口径)。

方案:① test() 四断言改为 self.run(...) 真实执行(输出重定向到构建目录日志文件后再做文件内容断言,断言语义不变);② 配方补 export_sources() 与 build() 首行补丁应用调用(本包补丁集为空,no-op,但结构对齐同类配方)。

经验:静态门禁只认形态不认行为——"真实执行"必须用门禁认识的 API 形态表达;先跑门禁再 finish,别在终态上被静态扫描打回。

4. 验证(分层,禁止相加)

口径声明:本节各层(4.1 上游测试 / 4.2 断言移植 / 4.3 私有 API / 4.4 消费者测试 / 4.5 产物核验 / 4.6 CI 与评审)分别统计、分别陈述,层与层之间不做相加、改名、拼接——混在一起说就是造假。下文所有百分比只挂各自分母。

4.1 上游测试实跑(R51 三步法台账)

第一步:静态清点。对上游 3.31.12 源码树全量 grep:add_test( 调用 977 处(含 1 处在宏定义内)→ 净注册点 976;RunCMake 测试子目录 347 个;平台门控 if(WIN32)/if(APPLE)/if(HPUX) 126 处。

第二步:实跑注册数。ctest -N 实测注册 644 项(“Total Tests: 644”,两轮独立构建树输出一致)。976→644 差额 332 = 上游自身平台/工具 if() 条件守卫(126 平台分支 + 子目录注册条件 + python3/Qt5/clang-tidy/valgrind/X11 等工具依赖门控)——全部是上游显式守卫,非适配遗漏。

第三步:四轮实跑演进(口径:全量 644 项,-j8,单测超时 900s):

轮次环境通过/失败通过率说明
R01旧树,无宿主模拟344 / 30053.4%300 失败 = 220 宿主检测衍生 + 80 稳定失败 + 0 新增(run7×run8 交叉验证)
R02旧树 + sysname 模拟564 / 8087.6%220 项全恢复;80 = 79 稳定 + 1 flaky(CxxOnly,-j8 全量负载快败,隔离重跑通过 8.96s)
R03干净树 + sysname 模拟601 / 4393.3%恢复 37 项(旧树残留);43 = 40 Failed + 3 Subprocess aborted
R03b干净树 + sysname+release 双维度模拟621 / 2396.4%M 族 20/20 恢复;23 = 20 Failed + 3 Subprocess aborted;零回归

包含关系:R03b 的 23 项失败与 R03 的 43 项中的非 M 项逐项一致(23 ⊂ 43)——release 模拟只恢复 M 族,未引入任何新失败。

失败模式分类(8 族,机制与恢复条件见 3.4 坑 4 归因表):终口径 23 项 = 21 平台条件(P7+S8+T3+L1+O1+F1)+ 2 待查(A2)。0 项适配缺陷。

证据可复现性:全部 16 份日志(run7/8/9/10、两轮 ctest -N、configure/build、conan create 等)sha256 见台账 §6(原始日志按赛事要求留本地、未入 PR;最终轮 run10.log 与 23 项失败清单的 sha256 已登记于台账)。重跑命令见 4.7。

4.2 断言 1:1 移植执行

不适用——本库上游测试套件为自包含整体(CTestTestfile.cmake 单入口注册,RunCMake 驱动族自带构建脚本),已在 OHOS 上原样直接执行(ctest 原生跑,非断言移植)。621 项通过中的全部断言均为上游断言在 OHOS 上的原生执行。口径边界:4.2 的"断言被执行"在本库退化为"上游测试二进制被执行",本文按不适用申报,不虚构移植工作量。

4.3 私有 API 分类留证

不适用——cmake 3.31.12 为纯上游 C++17 工程,对外消费接口是 CLI(bin/cmake、bin/cpack、bin/ctest);制品为纯可执行包(无 lib/、无对外头文件),不存在消费者链接内部符号的场景,无私有 API 需要分类留证。

4.4 消费者测试(分级,全部真实执行)

L1 版本断言:cmake --version → cmake version 3.31.12。(声明:版本断言只证明"包里的 cmake 是 3.31.12",不能宣称功能完成。)

负例(核心断言):清空 LD_LIBRARY_PATH 执行 cmake/ctest/cpack 三件套 → 均 rc=0。这是本包存在的理由:动态链接、依赖加载器路径的产物在 HMDFS 上必死在这条(worker 上 HNP cmake 的 Error relocating …std::__n1… 即此模式)。

L2 真实 API + 结果断言(功能冒烟):用包内 cmake 对 3 行 CMakeLists 的 trivial C 工程做 configure + build + run,断言运行输出。这是"是可用的 cmake"与"只是活着的 --version"的分界线。

消费者工程完整代码(test_package/consumer/,可复现):

# consumer/CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(consumer C)
add_executable(consumer_app main.c)
/* consumer/main.c */
#include <stdio.h>
int main(void) {
  printf("cmake-consumer-ok:1\n");
  return 0;
}

构建驱动(test_package/conanfile.py 的核心逻辑,self.run 真实执行):包内 cmake 对 consumer/ 执行 cmake -S -B → cmake --build → 运行 consumer_app 并断言输出 cmake-consumer-ok:1;四件套断言(L1 版本 / 负例加载 / NEEDED 仅系统库 / L2 冒烟)输出重定向到日志文件后逐条做文件内容断言。

实测输出(conan create test_package 尾段,取自 conan-create3.log):

[100%] Linking C executable consumer_app
[100%] Built target consumer_app
cmake/3.31.12 (test package): consumer output: cmake-consumer-ok:1
cmake/3.31.12 (test package): ALL FOUR ASSERTIONS PASSED

结果:四件套 4/4 + 消费者用例 1/1 = 鸿蒙特有用例 5/5(100%),编译/链接/运行均通过。

4.5 产物核验(设计红线①:静态自包含)

项值
bin/cmake24238656 B(≈24 MB),NEEDED 仅 libc.so
bin/cpack24537664 B,NEEDED 仅 libc.so
bin/ctest25647680 B,NEEDED 仅 libc.so
lib/无(纯可执行包,无库产物)
share/cmake-3.31/模块/帮助/文档资源
动态依赖终态无 libstdc++/libc++/libunwind/libxml2 任何动态项
conan-verifyG0(E092 已知风险已在配方处理)/ G1 / G2 / G3 / G4 / L2 / L4 / L5 全 PASS
构建耗时conan create 全程约 15-30 分钟(估算口径;原始日志留本地,sha256 见台账 §6)

NEEDED 实测输出(完整命令见 4.7 ③):

  0x0000000000000001 (NEEDED)       Shared library: [libc.so]

实现手段:上游 CMakeLists 自身强制 BUILD_SHARED_LIBS=OFF,配方追加 -static-libstdc++(clang 下该标志选静态 libc++.a,进入三个二进制的链接行)。红线①达成:产物不依赖加载器路径——"worker 镜像均质性"风险在产物层消除,而非押在机器上。

在这里插入图片描述

图1:真机终端复核包内 cmake——版本正确(3.31.12)且 NEEDED 仅 libc.so。

4.6 CI 与评审

完整轮次表(每次 push 触发 CI 重评估,UTC+8):

轮次push 时间head结果说明
12026-10-23 17:151821c88178通过(合入时 CI 门禁全绿,PR API 回读)首次 push:配方 + test_package + 4 套知识草稿
22026-10-30 10:3902c8356702通过(合入时 CI 门禁全绿,PR API 回读)reports 裁剪至最终轮证据 + 同步 upstream/main
32026-10-30 17:30010f2dfbfd通过(合入时 CI 门禁全绿,PR API 回读)按赛事老师要求移除 reports/(原始证据留本地)+ 同步 upstream/main;最终 head

口径说明:平台 CI 构建编号未见于 API 返回(PR/commit API 回读核验),完整 CI 记录以 PR 页面 CI 标签为准;本文可验证范围内(push/合入门禁 API 记录)未见失败轮记录。

检视记录:首轮自动化评审(PC 鸿蒙化评审)FAIL——PR 模板格式问题:上游库测试用例统计表缺合计行。修订 PR 正文(补合计行)后通过;人工评审 ningyu405 approved(PR API 回读)。

合并记录:PR #18259,squash 合入 main,2026-11-03 15:15:33(merged_by ningyu405),关联 Issue #4685。
在这里插入图片描述

图2:PR #18259 合入页——"Merged"状态为本文核心主张(真实 PR 合入 main)的硬事实证据;不能证明 CI 各轮构建细节。

4.7 复现速查(3 条命令,均可直接照抄执行)

① 全量构建 + 消费者四件套(约 15-30 分钟):

cd /storage/Users/currentUser/ohpc-contest/cmake-3.31.12/build_in_harmonyos
conan create archives/c/cmake/3.31.12 -pr:h ci/conan/profiles/ohos-aarch64 -pr:b ci/conan/profiles/ohos-aarch64

期望输出(尾段):cmake/3.31.12 (test package): consumer output: cmake-consumer-ok:1 与 cmake/3.31.12 (test package): ALL FOUR ASSERTIONS PASSED,RC=0。

② 上游证据链复跑(644 项全量,约 48 分钟):

cd /storage/Users/currentUser/tmp/builds/cmake-3.31.12.work/build-test8
LD_PRELOAD=/storage/Users/currentUser/tmp/cmake-shim/libuname_linux.so PATH=$PWD/bin:$PATH ctest -j8 --timeout 900

期望输出:96% tests passed, 23 tests failed out of 644 与 Total Test time (real) = 2889.64 sec(实测值随负载浮动;23 项名单应与台账失败清单逐项一致)。

③ NEEDED 自包含复核(秒级):

/data/service/hnp/bin/llvm-readelf -d /storage/Users/currentUser/.conan2/p/b/cmakef96c351c38e0f/p/bin/cmake | grep NEEDED

期望输出(单行): 0x0000000000000001 (NEEDED) Shared library: [libc.so]

口径对应:命令① → 第 3 节配方构建 + 4.4(消费者 5/5);命令② → 4.1(621/644 = 96.4%,四轮演进终轮口径);命令③ → 4.5(NEEDED 仅 libc.so)。以上三条命令即本文结论面的完整复现集:构建可用性、上游测试口径、产物自包含,三者均可在真机上按此重新核验。

5. 知识沉淀

编号知识(错误签名 → 处理策略)来源坑位复用价值
E524(既有)HMDFS 上 os.access(X_OK)/shutil.which 不可靠,真实 exec 返回码是唯一判据坑 4(P 族归因引用)权限位类失败归因直接复用
DRAFT-ohos-cmake-unknown-system-unix-offSystem is unknown to cmake / ProcessWin32.c.*windows.h → 配方层显式 CMAKE_SYSTEM_NAME=Linux + 测试层 LD_PRELOAD uname 模拟 + 干净树坑 1一切带"宿主检测"的构建系统在 OHOS 上的适配
DRAFT-ohos-bionic-glibc-gap-compat-shimpthread_getaffinity_np 未声明 / sockaddr_storage 重定义 → __OHOS__ 守卫 weak 符号 shim + 上游支持的 cache 变量,零源码补丁坑 2glibc 专属符号/头文件冲突类(bionic 用户态通用)
DRAFT-ohos-cmake-uname-release-meminfoProblem parsing /proc/meminfo → 宿主模拟双维度(sysname+release),严格 stderr 断言归族处理坑 3宿主身份派生 + 严格 stderr 断言类失败
DRAFT-ohos-hmdfs-platform-conditionsOperation not permitted / Could not find ldconfig / failed to create link / bionic 加载器消息格式 → ledger 逐项归因 + exclude-regex 名单,不改上游期望坑 4HMDFS 平台条件归因总表(P/S/T/L/O/F 各族恢复条件)

草稿状态说明:4 套草稿(kv+md 成对,共 8 文件)随 PR 合入 main(2026-11-03,commit 02dfafff00);截至 2026-11-08(commit API 按文件回读核验:唯一触碰 commit 即合入 commit,无转正 commit)仍位于 knowledge/drafts/,待主编复核转正为 E 编号;编号与正式库无冲突(当前均以 DRAFT- 前缀区分)。

6. FAQ

Q1:通过率 96.4% 不是 100%,算适配成功?
路径 C(高难度挑战)的测试门禁是 ≥90%,96.4% 过线。更关键的口径:23 项失败中 21 项为平台条件(8 族逐项归因,每项有首错+机制+恢复条件)+ 2 项待查,0 项适配缺陷——失败不是"我们没修好",而是"平台暂时给不了这个条件"(如 HMDFS 忽略 chmod、无 ldconfig)。归因表见 3.4(4.1 同口径引用),每一项都可对照复跑。

Q2:用 -DCMAKE_SYSTEM_NAME=Linux 声明目标,是不是"冒充 Linux"欺骗 CMake?
不是欺骗,是目标环境声明。OHOS PC 的架构是 aarch64、内核 ABI 是 Linux,CMake 的 Platform/Linux.cmake 对它的描述(C 编译器行为、链接器行为、可执行文件格式)与实际一致;不声明的后果是 CMake 把它判为 unknown system、整条构建链走 Windows 实现——那才是真的错。已知限制也写清楚了:CMake 上游没有 Platform/HarmonyOS.cmake(v3.31.12 与 master 均 404 实证),显式声明 CMAKE_SYSTEM_NAME=Linux 的工程不受"unknown system"影响,本配方即此模式。

Q3:零源码补丁,这个"零"可信吗?
可信,且可核验:上游 tarball 的 SHA-256(5f3fd5a5…)与 Kitware 官方摘要文件逐项一致;conandata.yml 的 patches 为空集;适配全部发生在配方层(-D 选项组 + weak 符号兼容头 + 编译期 -include)与测试环境层(LD_PRELOAD 宿主模拟 + 干净构建树)——这些产物都在制品里或配方里,不碰上游一个字符。

Q4:test_package 会不会是"空壳测试"(只 print 不执行)?
不是。四个断言全部 self.run 真实执行并检查返回码与输出内容(版本输出、清空 LD_LIBRARY_PATH 后的 rc、readelf NEEDED 输出、冒烟工程运行输出);其中"清空 LD_LIBRARY_PATH 后 rc=0"这条是负例级断言——动态依赖加载器路径的产物在 HMDFS 上必死在这条。这个形态本身还踩过坑:门禁对"真实执行"做静态扫描,subprocess.run 形态被判空壳(3.5 坑 5),改 self.run 后通过。

Q5:976 个 add_test 怎么就跑了 644 个?是不是漏了?
没漏。976→644 差额 332 是上游自身的平台/工具 if() 条件守卫:126 处平台分支(WIN32/APPLE/HPUX 等)+ 子目录注册条件 + python3/Qt5/clang-tidy/valgrind/X11 等本机不存在的工具依赖门控——全部是上游显式守卫,Linux CI 上这些守卫同样生效。清点与实跑注册数的口径差异在 4.1 第一步/第二步写明,ctest -N 两轮独立树输出一致(644)。

Q6:本包发布后,下游真的会受益吗?
受益机制是确定的:本包 NEEDED 仅 libc.so(4.5),不依赖加载器路径——worker 上"哪台机器自带能跑的 cmake"的均质性赌注被产物层消除(HNP cmake 因缺 libc++ 动态链接加载失败已 2 轮 CI 实证,见第 1 节)。下游 clickhouse/26.7.6.57-stable 加 tool_requires("cmake/3.31.12") 是独立步骤,须待本包 ohpcd 发布完成后执行(本文范围止于 PR 合入,见声明)。

7. 总结

7.1 量化盘点

维度数字
源码补丁0(tarball SHA-256 与官方摘要一致,patches 空集)
平台差异点6 个(2.2 差异表)
适配问题坑位5 个(第 3 节逐坑四段式)
上游测试644 项注册 / 621 通过 / 23 失败(96.4%);23 = 21 平台条件 + 2 待查,0 适配缺陷
消费者测试(鸿蒙特有)5/5(四件套 4/4 + 消费者用例 1/1,100%)
产物3 个可执行(cmake/cpack/ctest),NEEDED 仅 libc.so
知识沉淀4 套草稿(随 PR 入仓待转正)+ 消费既有知识 E524
CI3 轮 push(最终轮绿,合入门禁全绿)
PR#18259,2026-11-03 squash 合入 main

7.2 可复用方法(4 条)

  1. 宿主模拟双维度:OHOS 上凡测试/构建依赖宿主身份(uname sysname/release),LD_PRELOAD 拦截 libc 函数层(不是命令行工具层);模拟 release 时先全树 grep 回归论证"无测试读取该值"再动手。
  2. ledger 逐项归因(attribute-and-exclude):失败项逐项记"首错原文 + 机制 + 恢复条件",用 exclude-regex 名单跑排除口径;不改上游测试期望值——改期望是掩盖,归因是修复。
  3. 静态自包含红线:构建工具类产物必须过"清空 LD_LIBRARY_PATH + NEEDED 仅系统库"双重断言;-static-libstdc++(clang 选静态 libc++.a)是 OHOS 上的标准杆。
  4. 干净树约束:构建系统有宿主状态持久化文件(CMakeSystem.cmake 等),任何宿主身份修复后必须干净树重验,旧树结论不可信。

7.3 流程 checklist(通用)

  • 适配前:conan download <pkg>/<ver> -r=ohpcd --only-recipe 核实在仓状态
  • 适配前:通读 profile(PATH/LD_LIBRARY_PATH 注释是池子环境的"事故墓志铭")
  • 首个真实错误:先记录现象 + 完整报错原文,再定位机制
  • 误判:记录失败尝试进 notes.md(含无效原因),防止重走弯路
  • 测试:先静态清点 → 再实跑 → 后逐项归因(R51 三步法)
  • 状态怀疑:怀疑旧状态残留 → 先干净树重验
  • 产物:NEEDED 等价核验(静态自包含)
  • 知识:可泛化的"错误签名 + 机制 + 对照组"沉淀为草稿对(kv+md)

7.4 后续工作

  1. 上游 Platform/HarmonyOS.cmake——可另行向上游提 PR,消除"unknown system"根因(不在本赛事任务范围)。
  2. 2 项待查(FileAPI codemodel installers 计数差异、get_filename_component(PROGRAM) 空返回)——跟踪上游 Linux 环境对照后定论。
  3. ohpcd 发布完成后,下游 clickhouse/26.7.6.57-stable 加 tool_requires("cmake/3.31.12")(独立步骤,独立 PR)。

参考链接

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐