本文是「开发者大学生创新大赛 · OpenHarmony PC 三方库积分赛」的一篇适配复盘。对象是 Modbus 主站命令行工具 mbpoll 1.5.4,对应 PR:build_in_harmonyos!2310,2026-07-15 提交,2026-07-24 合入 main 并发布到制品仓。

这个库代码量不大,但把积分赛里最常见的几类坑几乎踩了一遍:依赖库没发布到制品仓、上游构建脚本依赖 git 元数据、依赖库版本落后于调用方 API、musl 工具链缺内核头、老 config.guess 不认 HarmonyOS、HMDFS 文件系统的 umask 陷阱、CI 门禁与 worker 基础设施抖动。所以拿它来复盘,比拿一个"一次 conan create 就过"的库更有参考价值。


ounter(counterh2一、背景:为什么是 mbpoll

1.1 这个库是干什么的

mbpoll 是一个 Modbus 主站(Master)仿真工具,命令行一条指令就能读写 Modbus 从站的线圈、离散输入、保持寄存器和输入寄存器,支持 RTU(串口)和 TCP 两种传输。工控、能源、楼宇自控这些场景里,拿它当"Modbus 版的 curl"来调设备非常顺手。

  • 上游:https://github.com/epsilonrt/mbpoll ,版本 1.5.4

  • 语言 / 构建:C,CMake

  • 许可证:GPL-3.0-only

  • 唯一运行时依赖:libmodbus ≥ 3.1.7

1.2 为什么选它

积分赛选库有一条朴素原则:选生态价值高、又没人做、且自己能兜住的库。mbpoll 满足三条:

  1. OpenHarmony PC 在工业和物联网场景要落地,Modbus 工具链是刚需,制品仓里当时连 libmodbus 都还没发布;

  2. 依赖只有一个 libmodbus,依赖树浅,适配周期可控;

  3. 上游自带 CTest 单元测试,PR 模板要求的"上游测试通过率"能拿到真实数据,而不是编一个 100%。

真做起来才发现,"依赖只有一个"和"依赖很好搞"是两回事。下面进入正题。


ounter(counterh2二、环境与工具链

项目说明
目标平台OpenHarmony / HarmonyOS PC,aarch64,musl libc
编译器CI worker 与本地构建容器:hnp 工具链下的 clang 15;真机复测:OHOS SDK clang 15.0.4
包管理Conan 2.x(conan create + test_package
本地验证团队内部的 hmbs-builder 构建容器(原生 aarch64,可直接运行编译产物)
真机复测HarmonyOS HAD 206.1.0 真机,OHOS SDK clang 15.0.4
CIbuild_in_harmonyos 仓库的 Buildbot worker,L0 结构门禁 + L1 测试执行门禁

有两点值得先说清楚,后面好几个坑都由它们引出:

  • 本地容器 uname 报 Linux,CI worker 报 HarmonyOS。 凡是 autotools 的 config.guess 探测平台,本地和 CI 走的分支不一样,本地绿了 CI 未必绿。

  • CI worker 的工作目录在 HMDFS(分布式文件系统)上,权限语义和普通 ext4 不同,某些"本地永远复现不了"的失败根源在这里。


ounter(counterh2三、动手前的侦察:先回答三个问题

在写第一行 conanfile 之前,我固定会先回答三个问题。这一步花 20 分钟,能省后面 2 小时。

问题 1:依赖在制品仓里吗?

archives/l/libmodbus/,有 3.1.10 和 3.1.12 两个目录,manifest 里 status: built。看起来像"已发布",但再往下看:没有 conanfile.py,也没有任何一个配方 requires。也就是说它只是被人编译通过过,从来没有作为 Conan 包发布到 ohpcd 制品仓。按仓库规则(R36:依赖必须先发布),我不能 self.requires("libmodbus/...")

教训:status: built ≠ 可 requires。判断标准是"制品仓里能不能 conan install 到",不是 manifest 上写了什么。

问题 2:上游构建系统有什么不可移植的地方?

tar tzf 看一眼 mbpoll 源码包,cmake/GitVersion.cmake 引起了注意:它靠 git describe --tags 取版本号,取不到就 FATAL_ERROR。Conan 的 source() 解出来的是源码 tarball,不是 git 仓库,这一步必挂。

问题 3:上游测试长什么样?

tests/ 下两个 C 文件 test_utils.ctest_serial.c,纯手写断言,走 CTest。没有 GTest、没有 Python 绑定、不需要网络和真实串口。结论:上游测试可以在容器里真跑,通过率能拿实数。

侦察完,适配方案的骨架就定了:在配方里 vendor libmodbus 源码静态编译,打两枚补丁,上游测试本地实跑取数。


ounter(counterh2四、核心难点:libmodbus 没发布,怎么办

4.1 三条路的取舍

方案优点问题
A. 先单独适配发布 libmodbus,再 requires最规范要等另一个 PR 合入并发布,周期不可控;且 libmodbus 3.1.12 源码归档需要 autogen(见下)
B. 假设 CI worker 系统里有 libmodbus零工作量worker 环境异质,随时链接失败;仓库明令禁止
C. 在配方内 vendor 源码,编成静态库链进 mbpoll自包含、可过 CI、产物只有一个二进制配方复杂度上升;要处理子项目的一堆移植问题

选 C。这也是后来沉淀成知识条目 E215 的"vendored 依赖构建到 staging"模式。

4.2 一个意想不到的坑:源码包从哪下

libmodbus 有两处下载源,差别很大:

  • github.com/stephane/libmodbus/archive/refs/tags/vX.Y.Z.tar.gz:GitHub 自动生成的源码归档,只有 autogen.sh,没有 configure。而构建容器和 CI worker 都没有 autoconf/automakeautogen.sh 跑不了。

  • libmodbus.org/releases/libmodbus-X.Y.Z.tar.gz:官方发布包,自带预生成的 configure。但笔者 2026 年 7 月实测,只有 3.1.6 和 3.1.7 能下,3.1.8 及以上全部 404

mbpoll 要求 libmodbus ≥ 3.1.7,3.1.7 刚好卡在门槛上。于是 conandata.yml 里第二个源就是它:

# sha256 已截断,以仓库中的 conandata.yml 为准
sources:
  - url: "https://github.com/epsilonrt/mbpoll/archive/refs/tags/v1.5.4.tar.gz"
    sha256: "a9bcc3af...9956f"
  - url: "https://libmodbus.org/releases/libmodbus-3.1.7.tar.gz"
    sha256: "7dfe9584...f8fbd"
    folder: "libmodbus"

source() 里两份源分别解压:mbpoll 到源码根,libmodbus 到 libmodbus/ 子目录。

4.3 vendor 构建的骨架

整体流程:build() 里先用 autotools 把 libmodbus 编成静态库、make install 到 build 目录下的 libmodbus-root/,再让 mbpoll 的 CMake 通过 PKG_CONFIG_PATH 找到它。

def generate(self):
    tc = CMakeToolchain(self)
    tc.variables["BUILD_TESTING"] = False
    tc.variables["MBPOLL_CUSTOM_RTS_GPIO"] = False   # piduino GPIO,OHOS 没有
    tc.cache_variables["CMAKE_SKIP_RPATH"] = True     # 静态链接无需 RPATH,也绕开 CMakeRelink.dir
    tc.generate()

    # 把 vendored libmodbus 的 .pc 暴露给 mbpoll 的 pkg_check_modules()
    lm_pkgconfig = os.path.join(self.build_folder, "libmodbus-root", "lib", "pkgconfig")
    env = Environment()
    env.define_path("PKG_CONFIG_PATH", lm_pkgconfig)
    env.vars(self, scope="build").save_script("conan_libmodbus_pkgconfig")

def build(self):
    apply_conandata_patches(self)
    self._build_libmodbus()
    cmake = CMake(self)
    cmake.configure()
    cmake.build()

整个流程画成一张图:

mbpoll 配方的 vendored libmodbus 构建流程:两份源码、三处补丁/shim、libmodbus 静态编译到 staging、mbpoll 经 PKG_CONFIG_PATH 链接、产物仅 bin/mbpoll

mbpoll 配方的 vendored libmodbus 构建流程:两份源码、三处补丁/shim、libmodbus 静态编译到 staging、mbpoll 经 PKG_CONFIG_PATH 链接、产物仅 bin/mbpoll

图 1:vendored 依赖构建流程(E215 模式)

_build_libmodbus() 是整个配方的重头戏,里面塞了四个移植修复,下一节逐个拆。

最终产物只有 bin/mbpollreadelf -d 只看到 NEEDED libc.so,libmodbus 已经静态链进去了。这样既不用给额外的 .so 做代码签名,也不用操心运行时 LD_LIBRARY_PATH


ounter(counterh2五、逐个拆坑

坑 1:上游用 git tag 取版本号,源码包构建直接中止

现象cmake.configure() 阶段报 FATAL_ERROR,来自 cmake/GitVersion.cmake

根因GetGitVersion()git describe --tags,源码 tarball 不是 git 仓库。更阴险的是:即使构建树的上层目录恰好是一个不相关的 git 仓库,它也会因为找不到匹配的 tag 而 FATAL_ERROR,所以"把源码解压到某个 git 仓库下面"并不能绕过去。

修法:补丁 0001-hardcode-version-no-git-dep.patch,把版本硬编码,直接 file(WRITE) 生成 version-git.h

set(MBPOLL_VERSION_MAJOR 1)
set(MBPOLL_VERSION_MINOR 5)
set(MBPOLL_VERSION_PATCH 4)
set(MBPOLL_VERSION_STRING "${MBPOLL_VERSION_MAJOR}.${MBPOLL_VERSION_MINOR}.${MBPOLL_VERSION_PATCH}")
file(WRITE  ${CMAKE_CURRENT_BINARY_DIR}/version-git.h "#define VERSION \"${MBPOLL_VERSION_STRING}\"\n")
file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/version-git.h "#define VERSION_SHORT \"${MBPOLL_VERSION_STRING}\"\n")
# ... VERSION_TINY / VERSION_MAJOR / VERSION_MINOR / VERSION_PATCH / VERSION_SHA1

这类"构建可重现性"补丁是积分赛里最常见的一类,凡是上游 CMake/Makefile 里出现 git describegit rev-parse,提前打掉。

坑 2:调用方用了依赖库还没有的 API

现象:libmodbus 编好了,mbpoll 编译报 MODBUS_QUIRK_MAX_SLAVEMODBUS_QUIRK_REPLY_TO_BROADCASTmodbus_enable_quirks 未声明。

根因:mbpoll 1.5.4 的 -Q / -X 选项调用 libmodbus 的 quirks API,这套 API 是 3.1.7 之后的 3.1.x 才加的(3.1.12 确认有),我们能拿到的带 configure 的发布包只到 3.1.7。典型的"调用方比依赖新"的版本错位。

为什么不能 #ifdef:quirks 是 enum 值和函数,不是宏,#ifdef MODBUS_QUIRK_MAX_SLAVE 探测不到。

修法:补丁 0002-make-libmodbus-quirks-optional.patch,用自定义宏守卫这段代码:

#if defined(MBPOLL_ENABLE_LIBMODBUS_QUIRKS)
  if (ctx.bEnableMaxSlaveQuirk || ctx.bEnableReplyToBroadcastQuirk) {
    int iQuirks = 0;
    /* ... modbus_enable_quirks(ctx.xBus, iQuirks) ... */
  }
#endif

本构建不定义该宏,-Q / -X 可以解析但是空操作。这是一个明确记录在 notes.md 和 PR 里的已知限制,不影响核心的 Modbus 读写。评审最反感的是"偷偷砍功能",写清楚了就是合理取舍。

坑 3:musl 工具链没有 linux/serial.h

现象:编 libmodbus 的 modbus-rtu.clinux/serial.h: No such file or directory

根因:OHOS 工具链基于 musl,不带 Linux 内核 UAPI 头。modbus-rtu.cHAVE_DECL_TIOCSRS485 下为 RS485 的 ioctl 包含了这个头,用到 struct serial_rs485SER_RS485_* 常量。

修法:这个结构体是内核 ABI,定义极其稳定,配方里直接 save() 一个最小 shim,编 libmodbus 时 -I 进去:

_LINUX_SERIAL_H = """\
#ifndef _LINUX_SERIAL_H
#define _LINUX_SERIAL_H
#include <stdint.h>

struct serial_rs485 {
    uint32_t flags;
    uint32_t delay_rts_before_send;
    uint32_t delay_rts_after_send;
    uint32_t padding[5];
};

#define SER_RS485_ENABLED        (1u << 0)
#define SER_RS485_RTS_ON_SEND    (1u << 1)
#define SER_RS485_RTS_AFTER_SEND (1u << 2)
#define SER_RS485_RX_DURING_TX   (1u << 4)
#define SER_RS485_TERMINATE_BUS  (1u << 5)
#endif
"""

shim = os.path.join(self.build_folder, "include_shim")
save(self, os.path.join(shim, "linux", "serial.h"), _LINUX_SERIAL_H)
os.environ["CFLAGS"] = "-I{} -O2".format(shim)

用 shim 而不是把 HAVE_DECL_TIOCSRS485 关掉,是为了保留 RS485 功能,只补缺失的声明,不改行为。

坑 4:老 config.guess 不认识 HarmonyOS

风险:这是知识库 E007 记录的坑,之前在其它 autotools 库上踩过:本地容器 configure 一切正常,CI worker 上 config.guess 报无法识别平台。这次在首次提交就做了规避,所以本 PR 的 CI 没有为它失败过。

根因:就是第二节说的 uname 差异。libmodbus 3.1.7 自带的 config.guess 早于 OpenHarmony,在 uname -s 返回 HarmonyOS 的 worker 上会识别失败。本地容器 uname 是 Linux,所以本地永远复现不了。

修法:显式传三元组,让 configure 根本不去调 config.guess

arch_cpu = "aarch64" if str(self.settings.arch).startswith("arm") else str(self.settings.arch)
triplet = "{}-unknown-linux-gnu".format(arch_cpu)
self.run('./configure --disable-shared --enable-static --prefix="{}" '
         '--build={t} --host={t} --quiet'.format(lm_prefix, t=triplet), cwd=lm_src)

这里有个细节:--build--host 传同一个值,configure 会当成原生构建而不是交叉构建,不会去找 aarch64-unknown-linux-gnu-gcc 这种带前缀的工具。aarch64-unknown-linux-gnu 是标准三元组,老 config.sub 校验能过。另一条路是替换整份带 OHOS 支持的 config.guess/config.sub(知识库 E007),对只有一个子项目的场景,传三元组更轻。

坑 5:HMDFS 上 config.status 建目录后自己没写权限

风险:这是知识库 E011 记录的坑,同样是首版配方就预先处理、本 PR 未在 CI 上实际触发。此前在其它 autotools 库的 CI 日志里,它表现为 ./config.status: line NNN: ./confXXXXXX/subs1.awk: Permission denied,紧接着 could not setup config files machinery

根因:autoconf 生成的 config.statusumask 077 && mktemp -d ./confXXXXXX 建临时目录(mode 700)。CI worker 的工作目录在 HMDFS 上,实测表现是目录 owner 对自己 mode 700 的目录没有写权限。本地容器是普通文件系统,永远不复现。

修法:configure 跑之前把 umask 077 / umask 000 全改成 umask 022

def _relax_umask(path):
    with open(path) as f:
        content = f.read()
    content = content.replace("umask 077", "umask 022").replace("umask 000", "umask 022")
    with open(path, "w") as f:
        f.write(content)

for fn in ("configure", "config.status"):
    p = os.path.join(lm_src, fn)
    if os.path.isfile(p):
        _relax_umask(p)

补充:mbpoll 用 022 在 CI worker 和 HAD 真机复测都通过了。但笔者在另一台 devbox 真机上适配别的库时遇到过更极端的情况:HMDFS 上新建文件的 owner uid 与进程 uid 不一致,进程靠 group 权限访问,022 也不够,得 002 保留 group 写权限。遇到 022 仍报 Permission denied 时可以往这个方向查。

坑 6:环境变量泄漏进主项目

这个不是报错,是差点埋雷。编 libmodbus 时我通过 os.environ 设了 CCCFLAGS=-I<shim> -O2。如果不还原,后面 mbpoll 的 CMake 构建会继承这套 CFLAGS,shim 的 -I 混进主项目,将来查问题会非常迷惑。

saved = {k: os.environ.get(k) for k in ("CC", "CFLAGS")}
os.environ["CC"] = cc
os.environ["CFLAGS"] = "-I{} -O2".format(shim)
try:
    self.run("./configure ...", cwd=lm_src)
    self.run("make -j2", cwd=lm_src)
    self.run("make install", cwd=lm_src)
finally:
    for key, prev in saved.items():
        if prev is None:
            os.environ.pop(key, None)
        else:
            os.environ[key] = prev

顺带一提 cc 的来源:self.conf.get("tools.build:compiler_executables", check_type=dict),从 Conan profile 里取,而不是读 shell 的 $CC。这是仓库规则 R33。

坑 7:两个小的 CMake 开关

  • MBPOLL_CUSTOM_RTS_GPIO=OFF:上游可选依赖 piduino 做 GPIO 控制 RS485 的 RTS 引脚,OHOS PC 没有这套设施,关掉。

  • CMAKE_SKIP_RPATH=ON:本仓构建环境下 cmake 在 install 阶段做 RPATH 重链接会在 CMakeRelink.dir 上失败。我们静态链接不需要 RPATH,直接跳过。


ounter(counterh2六、测试:拿真数据,不编数据

PR 模板要填"上游测试用例通过率"。我的原则是:通过率必须来自实跑,分母必须是上游总数

6.1 上游 33 个单元测试本地实跑

配方本身 BUILD_TESTING=OFF(上游测试没接进 CI 门禁),在构建容器里单独打开跑:

测试程序计数方式通过 / 总数覆盖内容
test_utils.c10 个测试函数10 / 10iGetIntList 整数列表解析,lSwapLong / fSwapFloat 字交换
test_serial.c程序自计数 g_tests_run23 / 23串口参数枚举转字符串(flow / databits / stopbits / parity)
合计33 / 33(100%)
$ ./bin/test_utils    → All tests passed.                                (exit 0)
$ ./bin/test_serial   → Tests run: 23, Tests failed: 0, ALL TESTS PASSED (exit 0)
$ ctest               → 100% tests passed, 0 tests failed out of 2

6.2 反向验证:确认不是假绿

"全过"最怕的是 harness 根本没在检查。我把 test_serial 换成一个 exit 1 的桩重跑 ctest,得到 test_serial (Failed),说明 CTest 真能抓到失败。这一步只要 30 秒,建议每个库都做。

6.3 test_package 烟雾测试

test_package 跑打好包的二进制,mbpoll -V 输出版本号并用正则校验:

#!/data/service/hnp/bin/bash
set -eo pipefail
MBPOLL="${1:?usage: test.sh <mbpoll binary>}"
"$MBPOLL" -V 2>&1 | head -3
"$MBPOLL" -V 2>&1 | grep -qE "[0-9]+\.[0-9]+"
echo "[ok] mbpoll executable runs and reports its version"

有个平台细节:OHOS 的 /system/bin/sh 是 mksh,不能直接执行 ELF,所以 test_package 的 conanfile 用 hnp 的 bash 驱动脚本,二进制路径从 self.dependencies["mbpoll"].package_folder 拼全路径,不依赖 PATH。

6.4 二进制安全检查

file bin/mbpoll   → ELF, arm64, dynamic (/lib/ld-musl-aarch64.so.1)
readelf -d        → NEEDED: libc.so        (libmodbus 已静态链入)
readelf -h        → Type: DYN (PIE)
readelf -l        → GNU_STACK(NX)+ GNU_RELRO

PIE、NX、RELRO 齐全。

6.5 真机复测

2026-07-19 在 HarmonyOS HAD 206.1.0 真机(OHOS SDK clang 15.0.4)上 conan create + test_package 原生跑通。这一步的意义在于:容器里的 hnp clang 是"同源"但不完全等价于真机 SDK clang,真机跑一遍才能说"适配完成"。

HarmonyOS HAD 真机上运行打包后的 mbpoll:-V 输出 1.5.4,readelf 仅见 libc.so,test.sh 烟雾测试通过

HarmonyOS HAD 真机上运行打包后的 mbpoll:-V 输出 1.5.4,readelf 仅见 libc.so,test.sh 烟雾测试通过

图 2:真机复测终端记录(按 notes.md 中的实测输出重绘)


ounter(counterh2七、CI 门禁与 PR 流转:一条真实的时间线

配方写完只是一半,PR 流转是另一半。这条 PR 的时间线很有代表性:

时间事件说明
07-15提交 PR,机器人给出变更摘要 + 代码审查(2 个 P3)未使用的 import、set -e 少了 -u,均为规范项
07-15CI 排队一直没有 worker 领取
07-16机器人提示"此 PR 从未触发 CI,自动重新排队"依旧没跑
07-17手动评论 /rebuild立刻被 worker CI-022 领取,首次 CI 通过(#5128)
07-18追加 E215 知识条目(路径 B),CI 再次通过,机器人二次审查覆盖了新增的知识文件
07-22一次 CI 失败于 git-clone 步骤纯 worker 基础设施问题,与配方无关
07-23/rebuild 被门禁拦下:"审核未通过 0/4,测试未通过 0/1"人工审核前置于 CI 触发
07-24门禁放行,CI 通过(#9962,CI-007);上午一次 checkout-pr 抖动(#9993)后,REL-010 跑发布流水线 #320:conan-upload + sign-artifacts 全绿合入 main,制品仓可用

几条经验:

  1. CI 排队超过一天不动,直接评论 /rebuild 排队系统会丢任务,等是等不来的。

  2. 失败先看失败在哪一步。 失败在 git-clonecheckout-prworker-prep 的,是基础设施抖动,等下一次 rebuild 即可;只有 conan-build-test 失败才是配方的问题。机器人的"失败诊断"分类经常是"未知",要点进完整日志看。

  3. 机器人的状态评论是原地改写的(多个 PR 观察到的现象)。 同一条评论会被后续构建反复更新,"失败"不是终态,别急着改代码。

  4. 人工审核是 CI 的前置门禁。 4 位审核 + 1 位测试都要打勾,之后 CI 才会自动触发,所以 PR 描述要把"为什么这样做"写清楚,审核人不用追着问。

ohpcd-robot 的 CI 校验通过评论:构建 #9962,L0/L1 门禁与 test_package 全部通过

ohpcd-robot 的 CI 校验通过评论:构建 #9962,L0/L1 门禁与 test_package 全部通过

图 3:CI 校验通过评论(按 PR #2310 评论原文重绘)


ounter(counterh2八、知识沉淀:从一次适配到一条可复用的模式

积分赛"路径 B(贡献创新)"鼓励把适配经验沉淀为知识图谱条目。这次沉淀的是 E215 — vendored/bundled 依赖的构建模式

  • 触发条件:库依赖某个未发布到制品仓、仓库内也无配方的库,但可以拿到其源码。

  • 推荐方案build() 内先把子项目 configure --disable-shared --enable-static --build=<triplet> 编成静态库,make installbuild_folder/<dep>-root 的 staging 目录;主库通过 -I/-LPKG_CONFIG_PATH 指向 staging。

  • 禁止做法:假设 worker 系统里有这个库;把 vendored 依赖编成 .so 动态链接(要额外签名、要 LD_LIBRARY_PATH);手工 copy .o/.a 进产物。

  • 验证方式readelf -d <产物> | grep NEEDED 只应看到 libc.so 之类的系统库。

同一模式覆盖了 mbpoll(libmodbus)、logrotate(popt)、log4cxx(apr + apr-util + expat)、tmate(libssh + msgpack + mbedtls + ncurses)四个库的适配,一条经验多次复用,比多适配一个库更值。


ounter(counterh2九、复盘总结

回头看,mbpoll 本身没有一行代码需要为鸿蒙"改逻辑",所有工作都在构建层和依赖层。这也是 PC 三方库适配的常态:难点不在目标库,在它周围的生态缝隙。

给后来者的 checklist:

  • [ ] 依赖是否真在制品仓?看能不能 conan install,别信 status: built

  • [ ] 源码包里有没有 git describegit rev-parse?提前打补丁硬编码版本

  • [ ] 依赖版本能否满足调用方 API?enum/函数不能 #ifdef,用自定义宏守卫并记录限制

  • [ ] musl 缺哪些内核头?能用最小 shim 补声明的,不要关功能

  • [ ] 有 autotools 子项目?显式 --build/--host 三元组,绕开老 config.guess

  • [ ] 先在容器验证,再想一遍"CI worker 的 uname 和 HMDFS 会不会让这一步分叉"

  • [ ] umask 077022(E011)

  • [ ] 子项目的 CC/CFLAGS 用 try/finally 还原,别泄漏进主项目

  • [ ] 上游测试实跑取数,分母用上游总数,再做一次反向验证防假绿

  • [ ] readelf -d 看 NEEDED,readelf -h/-l 看 PIE/NX/RELRO

  • [ ] CI 排队超一天就 /rebuild;失败先看是哪一步

  • [ ] 能沉淀成通用模式的,写成知识条目

ounter(counterh2附:PR 文件清单

archives/m/mbpoll/
├── README.md                     # 适配说明
├── commands.json                 # 向制品仓注册 mbpoll 命令行工具
├── manifest.yaml                 # 软件级 manifest
└── 1.5.4/
    ├── conanfile.py              # 配方:vendor libmodbus 静态编译 + CMake 构建 mbpoll
    ├── conandata.yml             # 两个源 + 两枚补丁
    ├── manifest.yaml             # 版本级 manifest,含真机复测记录
    ├── notes.md                  # 适配笔记、测试实跑记录、已知限制
    ├── patches/
    │   ├── 0001-hardcode-version-no-git-dep.patch
    │   └── 0002-make-libmodbus-quirks-optional.patch
    └── test_package/
        ├── conanfile.py          # 跑打包后的二进制
        ├── test.sh               # mbpoll -V 烟雾测试
        └── MANIFEST.yml          # 上游测试声明(33 个断言)
knowledge/graph/draft/
├── E215-draft-9E9A.kv            # 知识条目(结构化)
└── E215-draft-9E9A.md            # 知识条目(说明文档)

PR 链接:https://gitcode.com/OpenHarmonyPCDeveloper/build_in_harmonyos/merge_requests/2310 上游仓库:https://github.com/epsilonrt/mbpoll

Logo

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

更多推荐