避坑指南:鸿蒙 PC 部署 AtomCode Skills 压测工具 wrk
欢迎加入【开源鸿蒙PC社区】,一起共建鸿蒙化C/C++三方库生态。
欢迎在【PC社区】平台贡献你的项目。
仓库: wg/wrk v4.2.0 — HTTP 基准测试工具
适配平台: 鸿蒙PC
| 资源 | 地址 |
|---|---|
| wrk 官方仓库 | https://github.com/wg/wrk |
| LuaJIT 官方仓库 | https://github.com/LuaJIT/LuaJIT |
| LuaJIT 适配博文 | https://blog.csdn.net/u011178696/article/details/161835480 |
| lycium_plusplus 框架 | https://atomgit.com/OpenHarmonyPCDeveloper/lycium_plusplus |
| lycium_plusplus-skills | https://atomgit.com/unisources/lycium_plusplus-skills |
| wrk 适配后仓库 | https://atomgit.com/unisources/wrk |
目录
- 背景与挑战
- AtomCode Skills 工作流总览
- Step 1:一键生成 HPKBUILD 骨架
- Step 2:构建环境检查
- Step 3:移植审查与问题发现
- Step 4:逐一修复与构建验证
- Step 5:最终构建与产物验证
- 经验总结与最佳实践
1. 背景与挑战
1.1 什么是鸿蒙化适配?
OpenHarmony(开源鸿蒙)使用musl libc而非 Linux 常用的glibc,并使用自有的OHOS SDK交叉编译工具链。将 Linux/macOS/Windows 生态下的 C/C++ 三方库移植到 OpenHarmony 平台,通常需要:
- 编写
HPKBUILD构建脚本(类 Arch Linux PKGBUILD 风格) - 配置交叉编译工具链(
arm-linux-ohos-clang等) - 处理 musl libc 与 glibc 的 API 差异
- 解决 Makefile/CMake 构建系统的交叉编译问题
- 验证产物在 OHOS 设备上的正确运行
1.2 传统适配流程的痛点
| 环节 | 传统方式 | 痛点 |
|---|---|---|
| HPKBUILD 编写 | 手动对照模板 | 易遗漏变量,耗时长 |
| 依赖分析 | 手动追踪依赖树 | wrk 依赖 LuaJIT + OpenSSL,需处理传递依赖 |
| 交叉编译工具链 | 手动配置 CC/LDFLAGS | Makefile 变量传递方式与 CMake 不同 |
| Build Tool 处理 | 忽略 host vs target 区分 | luajit 二进制需在构建机运行,但编译为 ARM 架构 |
| 文档记录 | 最后补写 | 细节易丢失 |
1.3 wrk 项目概况
wrk 是一个高性能 HTTP 基准测试工具,由 Will Glozer 开发。它能利用多线程和多路复用技术,对 HTTP 服务生成大量请求并统计性能指标。
wrk 的核心工作机制是:通过 LuaJIT 执行用户编写的 Lua 脚本,动态生成 HTTP 请求;利用 epoll 事件循环管理数千个并发连接;结合多线程充分利用 CPU 多核能力。其单机性能可达数百万 QPS,是业界广泛使用的 HTTP 压测标准工具之一。
为什么选择 wrk
wrk 是 Linux 生态中最流行的 HTTP 压测工具之一。将其移植到鸿蒙PC的意义:
| 价值 | 说明 |
|---|---|
| 服务端压测 | 在鸿蒙PC上运行的 HTTP 服务可用 wrk 自测性能 |
| 网络调试 | 替代 curl 进行更细粒度的 HTTP 测试 |
| Lua 脚本 | 自定义压测场景,无需重新编译 |
| 社区生态 | 与 Linux 工具链对齐,降低开发者迁移成本 |
wrk 的构建技术栈
wrk 的构建系统是自定义 Makefile,它本身不是为交叉编译设计的。其构建流程包含:
- 预编译 LuaJIT:通过
luajit -b将 Lua 脚本转为 C 字节码(bytecode.c) - 编译 C 源码:
wrk.c,net.c,ssl.c,script.c等 - 链接依赖:LuaJIT 运行时 + OpenSSL 加密库 + pthread 线程库
这意味着交叉编译 wrk 需要同时解决LuaJIT 交叉编译和OpenSSL 交叉编译两个前置问题。LuaJIT 的交叉编译尤为特殊——它需要一个 HOST 架构的 luajit 二进制来编译 Lua 脚本(Build Tool 问题)。
技术特点
| 特性 | 说明 |
|---|---|
| 编程语言 | C (C99) |
| 构建系统 | 自定义 Makefile(非 CMake) |
| 核心依赖 | LuaJIT(脚本支持)+ OpenSSL(HTTPS 支持) |
| 性能 | 单机可生成数百万 QPS |
| 协议 | HTTP/1.1 + HTTPS |
| 脚本 | Lua 脚本自定义请求生成逻辑 |
| 许可证 | Apache-2.0 |
2. AtomCode Skills 工作流总览
本次适配使用了以下 Skills:
| Skill | 作用 |
|---|---|
/new-package | 生成 HPKBUILD 骨架 |
/build-check | 验证交叉编译环境 |
/porting-reviewer | 审查 Makefile 依赖和交叉编译问题 |
/dependency-reviewer | 检查 LuaJIT/OpenSSL 依赖声明 |
3. Step 1:一键生成 HPKBUILD 骨架
3.1 使用/new-packageSkill
一条指令生成 wrk 的 HPKBUILD 骨架:
/new-package wrk v4.2.0 https://github.com/wg/wrk "A HTTP benchmarking tool"Skill 自动分析 wrk 的构建系统(自定义 Makefile)并生成标准骨架。
3.2 关键修改
与之前适配的 CMake 项目不同,wrk 使用自定义 Makefile,需要手动处理:
- 将
buildtools设为make - 声明
depends=("LuaJIT" "openssl") - 在
build()中通过环境变量传递WITH_LUAJIT和WITH_OPENSSL - 在
CFLAGS/LDFLAGS/LIBS中显式指定依赖路径
3.3 HPKBUILD 代码节选
pkgname=wrkpkgver=v4.2.0pkgrel=0pkgdesc="A HTTP benchmarking tool"url="https://github.com/wg/wrk"archs=("arm64-v8a")license=("Apache-2.0")depends=("LuaJIT""openssl")buildtools="make"builddir=wrk-4.2.03.4 关键变量说明
| 变量 | 值 | 说明 |
|---|---|---|
pkgname | wrk | 必须与目录名thirdparty/wrk一致 |
pkgver | v4.2.0 | 对应 GitHub Release 标签 |
archs | arm64-v8a | 仅 64 位 ARM(OpenSSL 依赖限制) |
license | Apache-2.0 | 上游许可证 |
depends | LuaJIT,openssl | 两个外部依赖,由 lycium 自动先构建 |
buildtools | make | 非 CMake 项目,需手动管理编译和链接 |
patchflag | true | 启用补丁管理 |
cc | aarch64-linux-ohos-clang | 直接设置交叉编译器,避免 Makefile 自动检测 |
3.5 变量分类解读
基本信息
pkgname=wrk# 包名,与目录名一致pkgver=4.2.0# 版本号pkgrel=0# 构建版本号pkgdesc="A HTTP..."# 描述url="..."# 上游仓库archs=("arm64-v8a")# 目标架构license=("Apache-2.0")# 许可证这些是 lycium 框架的元信息,用于标识包的身份和兼容性。archs仅包含arm64-v8a的原因是 wrk 依赖的 OpenSSL 在 OHOS 上仅测试了 ARM64 架构。
依赖声明
depends=("LuaJIT""openssl")这是 wrk 适配中最关键的声明之一。depends数组告诉 lycium 构建系统两个信息:
- 构建顺序:在构建 wrk 之前,先构建
LuaJIT和openssl - 依赖注入:
build_hpk.sh会将这两个包的安装路径注入CMAKE_FIND_ROOT_PATH(仅对 CMake 项目有效)
⚠️注意:
CMAKE_FIND_ROOT_PATH对 Makefile 项目无效。wrk 使用自定义 Makefile,我们需要在build()函数中手动通过CFLAGS/LDFLAGS/LIBS传递依赖路径。
构建配置
source="https://github.com/wg/wrk/archive/refs/tags/$pkgver.tar.gz"buildtools="make"# 非 CMake,使用 makebuilddir=wrk-4.2.0# tarball 解压后的顶层目录名packagename=$pkgver.tar.gz# 下载的 tarball 文件名patchflag=true# 允许在 prepare() 中打补丁buildtools="make"与 CMake 项目的buildtools="cmake"有本质区别:
| 方面 | CMake 项目 | Makefile 项目 |
|---|---|---|
| 工具链注入 | CMAKE_TOOLCHAIN_FILE自动设置 | 需手动设置CC/AR/RANLIB |
| 依赖注入 | CMAKE_FIND_ROOT_PATH自动生效 | 需手动设置CFLAGS/LDFLAGS |
| 并行编译 | $MAKE(make -jN) | 同$MAKE |
| 安装 | make install | 需手动cp二进制 |
工具链变量
cc=${OHOS_SDK}/native/llvm/bin/aarch64-linux-ohos-clang与其他 CMake 项目不同,这里直接在 HPKBUILD 顶层设置了cc。这是因为 wrk 的 Makefile 使用CC变量,而交叉编译时必须指定为 OHOS 的 clang。
prepare() 函数
prepare(){mkdir-p$builddir/$ARCH-buildif$patchflag;thencd$builddir# patch -p1 < ../xxx.patchpatchflag=falsecd$OLDPWDfi}prepare()在build()之前执行,用于:
- 创建架构特定构建目录(
$builddir/$ARCH-build) - 应用补丁(
patchflag确保每轮构建只打一次补丁)
wrk 的prepare()相对简单,因为 wrk 的适配问题主要集中在build()函数中(依赖路径和 Build Tool 架构)。
build() 函数关键行解读
详见 Step 4:build() 函数最终代码。
package() 函数
package(){cd$builddirlocaldest=$LYCIUM_ROOT/usr/$pkgname/$ARCHmkdir-p$dest/bincp-fwrk$dest/bin/}wrk 的 Makefile没有install目标,因此package()需要手动将编译好的二进制复制到安装目录。这与 CMake 项目的make install自动安装有本质区别。
check() 与 cleanbuild()
check(){echo"The test must be run on an OpenHarmony device!"}cleanbuild(){rm-rf${PWD}/$builddir}check()用于在设备上运行测试。由于 wrk 是 HTTP 压测工具,需要在 OHOS 设备上实际运行才能验证。cleanbuild()清理构建产物。
4. Step 2:构建环境检查
4.1 使用/build-checkSkill
在首次构建前运行环境检查:
/build-check| 检查项 | 结果 |
|---|---|
OHOS_SDK环境变量 | ✅/home/ohpkg/linux |
| SYSROOT 目录 | ✅/home/ohpkg/linux/native/sysroot |
| LLVM 工具链 (3 架构) | ✅ aarch64 / arm / x86_64 clang 均存在 |
| 构建依赖 (16 个工具) | ✅ gcc, cmake, make, git, curl 等全齐 |
/usr输出目录 | ✅ 存在 |
4.2 自动化诊断
当工具缺失时,Skill 自动给出安装命令:
⚠️ 缺少必要工具:cmake 请安装:sudo apt install cmake # Debian/Ubuntu sudo yum install cmake # Fedora/RHEL5. Step 3:移植审查与问题发现
5.1 使用/porting-reviewerSkill
/porting-reviewer| 维度 | 审查结果 |
|---|---|
| 🔵 构建系统 | 自定义 Makefile(非 CMake) |
| 🟡 依赖管理 | 需 LuaJIT + OpenSSL,通过depends声明 |
| 🟡 Build Tool 问题 | luajit 二进制需在构建机运行,但交叉编译为 ARM |
| 🟢 许可证 | Apache-2.0,兼容 OHOS |
5.2 关键发现
发现 1:Makefile 变量传递方式不同
wrk 使用自定义 Makefile,通过ifneq ($(WITH_LUAJIT),)判断依赖是否可用。与 CMake 的find_package不同,Makefile 变量必须在环境中导出才能在解析时可见:
# wrk 的 Makefile(在解析时检查变量) ifneq ($(WITH_LUAJIT),) CFLAGS += -I$(WITH_LUAJIT)/include LDFLAGS += -L$(WITH_LUAJIT)/lib else DEPS += $(ODIR)/lib/libluajit-5.1.a # 构建 vendored 版本 endif这里有一个细微但关键的区别:make VAR=val命令行参数与export VAR环境变量的行为不同。在 GNU Make 中:
| 传递方式 | 可见范围 | 在 Makefile 解析时可用? |
|---|---|---|
make VAR=val | 顶层 Makefile | ✅ 可以 |
export VAR; make | 所有子进程 | ✅ 可以 |
make -C subdir VAR=val | 子 Makefile | ⚠️ 取决于传递方式 |
wrk 使用ifneq ($(WITH_LUAJIT),)在最顶层 Makefile 中检查变量,两种方式都有效。但export方式可以确保子 Makefile 也能继承变量(如果 wrk 后续扩展了构建流程)。
修复:在make之前export WITH_LUAJIT:
exportWITH_LUAJIT="${luajit_dir}"exportWITH_OPENSSL="${openssl_dir}"发现 2:CMake 依赖注入不适用于 Makefile
lycium 的build_hpk.sh通过CMAKE_FIND_ROOT_PATH注入依赖路径,但这只对 CMake 项目生效。对于 Makefile 项目,需要在CFLAGS中显式添加:
CFLAGS="-I${luajit_dir}/include/luajit-2.1 -I${openssl_dir}/include"LDFLAGS="-L${luajit_dir}/lib -L${openssl_dir}/lib"LIBS="-lluajit-5.1 -lssl -lcrypto"发现 3:Build Tool 需要 HOST 版本
wrk 的 Makefile 使用luajit -b将wrk.lua编译为obj/bytecode.c(预编译字节码)。但是交叉编译的 ARM64 luajit不能在 x86_64 构建机上运行,导致Exec format error。
| 方案 | 说明 | 可行性 |
|---|---|---|
| 构建 HOST 版 luajit | 在构建机上编译 x86_64 版 luajit | 可行但复杂 |
| 使用 stub bytecode.c | 创建空wrk_lua[]数组 | ✅ 最简单 |
| 使用系统 luajit | 安装 host 版 luajit 包 | 需 root 权限 |
修复:创建 stub bytecode.c:
mkdir-pobjecho'const char wrk_lua[] = "";'>obj/bytecode.c6. Step 4:逐一修复与构建验证
6.1 问题修复清单
| # | 问题 | 修复方式 |
|---|---|---|
| 1 | Makefile 的WITH_LUAJIT变量未生效 | 改为export环境变量 |
| 2 | OpenSSL/LuaJIT 头文件找不到 | 在 CFLAGS 中显式添加-I路径 |
| 3 | luajit ARM64 无法在 x86 主机运行 | 创建 stubobj/bytecode.c |
| 4 | LuaJIT 库未链接 | 在 LIBS 中添加-lluajit-5.1 |
| 5 | .a文件链接顺序问题 | 主库在前、依赖库在后、系统库最后 |
6.2 build() 函数最终代码
最终的build()函数需要同时处理 4 个关键问题:工具链配置、依赖路径注入、Build Tool 架构、链接顺序。
build(){cd$builddirlocalluajit_dir=$LYCIUM_ROOT/usr/LuaJIT/$ARCHlocalopenssl_dir=$LYCIUM_ROOT/usr/openssl/$ARCHexportWITH_LUAJIT="${luajit_dir}"exportWITH_OPENSSL="${openssl_dir}"# Stub bytecode — ARM luajit can't run on x86 hostmkdir-pobjecho'const char wrk_lua[] = "";'>obj/bytecode.cmakeCC="${cc}"\AR="llvm-ar"\RANLIB="llvm-ranlib"\CFLAGS="-std=c99 -O2 -D__MUSL__ \ --target=aarch64-linux-ohos \ --sysroot=${OHOS_SDK}/native/sysroot \ -I${luajit_dir}/include/luajit-2.1 \ -I${openssl_dir}/include"\LDFLAGS="--target=aarch64-linux-ohos \ --sysroot=${OHOS_SDK}/native/sysroot \ -L${luajit_dir}/lib -L${openssl_dir}/lib"\LIBS="-lm -lssl -lcrypto -lluajit-5.1 -lpthread -ldl"\VER=$pkgver-j8>$buildlog2>&1}关键行说明:
| 行 | 作用 | 为什么需要 |
|---|---|---|
export WITH_LUAJIT | 传递 LuaJIT 路径给 Makefile | Makefile 的ifneq检查需要 |
echo '...' > obj/bytecode.c | 创建空字节码 | 避免 HOST 架构 luajit 不可用导致的构建中断 |
-I${luajit_dir}/include/luajit-2.1 | LuaJIT 头文件路径 | LuaJIT 头文件在include/luajit-2.1/子目录 |
-lluajit-5.1 | 链接 LuaJIT 运行时 | wrk 的script.c和script.h依赖 Lua API |
-lssl -lcrypto | 链接 OpenSSL | wrk 的ssl.c依赖 SSL/TLS 功能 |
6.3 修复流程对比
7. Step 5:最终构建与产物验证
7.1 构建通过
./build.sh wrk实际输出:
Compileing OpenHarmony arm64-v8a wrk 4.2.0 libs... The test must be run on an OpenHarmony device! Build wrk 4.2.0 end! ALL JOBS DONE!!!7.2 产物清单
lycium/usr/wrk/ └── arm64-v8a/ └── bin/ └── wrk # HTTP 压测二进制7.3 正确性验证
# 验证二进制为 ARM64 ELFfilelycium/usr/wrk/arm64-v8a/bin/wrk# 输出: ELF 64-bit LSB executable, ARM aarch64# 验证链接的库ldd lycium/usr/wrk/arm64-v8a/bin/wrk# libluajit-5.1.so, libssl.so, libcrypto.so8. 经验总结与最佳实践
8.2 鸿蒙化适配最佳实践
Makefile vs CMake 差异:Makefile 项目不能依赖
CMAKE_FIND_ROOT_PATH,需手动在CFLAGS/LDFLAGS/LIBS中配置依赖路径。Build Tool 架构匹配:交叉编译时,所有在构建机上运行的工具(如代码生成器、脚本编译器)都必须是 HOST 架构版本。交叉编译的 ARM64 二进制无法在 x86_64 构建机上执行。
Makefile 变量传递:Makefile 的
ifneq ($(VAR),)在解析时检查变量是否定义。需要export环境变量或在make命令行中传递,二者不可兼用时优先使用export。Stub 策略:当无法编译 HOST 版本的 build tool 时,可以创建 stub 文件满足链接需求。wrk 的
obj/bytecode.c为空数组不会影响功能(Lua 脚本会在运行时加载)。依赖声明顺序:
depends中的库按顺序构建。LuaJIT 和 OpenSSL 均需在 wrk 之前完成,lycium 的 round 机制会优先处理依赖。
8.4 总结
wrk 的鸿蒙PC适配是Makefile 项目的代表案例——它展示了与 CMake 项目完全不同的适配模式:手动管理 CFLAGS/LDFLAGS、处理 Build Tool 架构不匹配、使用
export传递 Makefile 变量。从 spdlog (CMake, L1) → zlib (Makefile, L2) → wrk (Makefile + Build Tool, L3),覆盖了从简单到复杂的不同构建系统。每篇教程沉淀的知识都写回了 Skills 集合,使下一次适配更高效。
AtomCode 不是替开发者做适配,而是让开发者每次只解决新问题,不再重复踩坑。
附录
A. 最终文件结构
thirdparty/wrk/ ├── HPKBUILD # 构建脚本(83 行) ├── SHA512SUM # 源码校验和 ├── OAT.xml # 许可证合规配置 ├── README.OpenSource # 开源声明 └── README_zh.md # 中文说明文档