本文主要描述如何通过 conan 1.x 交叉编译常见的三方库到鸿蒙平台,基础的环境配置及工程化集成配置,以及在编译过程中遇到过的问题。
在现有业务中,我们已经有了一些成熟的跨平台项目在运行,支持 Android、iOS、Windows、macOS、Linux like 系统,这些项目各自都依赖了大量的三方开源代码,如常见的 C 库 zlib、openssl、libcurl、libuv、sqlite3,C++ 库 jsoncpp、gtest 等。我们不可能为每个系统去单独维护编译脚本或产物,因为这个过程真的相当痛苦,你不仅需要了解不同的工程管理工具如 gn、cmake、autotools、makefile 等,还需要了解各个三方库的各类编译配置,做跨平台开发的同学一定能深有体会。
幸运的是我们已经在现有平台下通过 conan 全自动管理三方库产物,不需要刻意关心三方库的编译细节,只需要通过 conan 指定一个版本号让其自动帮我们编译即可。本文也将围绕这个话题,描述如何通过 conan 交叉编译常见的三方库到鸿蒙平台。通过本文你可以了解到:
- 如何快速编译一个鸿蒙的三方库(静态或动态)
- 如何配置驱动现有使用 conan 1.x 的项目支持鸿蒙系统
- 有哪些已经踩过的坑
- 写好的 Dockerfile 可直接创建一个带有 conan 的鸿蒙交叉编译环境用于 CI 集成
其中的优势在于:
- 除特别需要不需要修改 conan 或三方库代码
- 使用系统已有的环境如 cmake、conan 等,无需使用工具链中定制的程序
因为历史债务问题,我们虽然已经切换了部分项目到 conan 2.x,但还有一部分项目依然在使用 conan 1.x,的确是因为切换成本较高且有些项目上的定制功能尚未在 conan 2.x 中找到好的解决方案,所以通过 conan 1.x 交叉编译到鸿蒙也是目前我们必须要考虑的重点。
步骤演示
官方提供了多个平台的交叉编译工具链,我们这里以 Linux 平台交叉编译到鸿蒙举例,因为后续 CI 自动化集成使用 Linux 更方便我们做自动化部署。
配置交叉编译环境
首先登录华为开发者中心,下载完整的工具链压缩包:https://developer.huawei.com/consumer/cn/download/command-line-tools-for-hmos,下载时选 for HarmonyOS 的版本。

由于这个压缩包过于庞大,一些与交叉编译工具链无关的工具,我们在解压时可以过滤掉,真正需要的只有 command-line-tools/sdk/default/openharmony/native
目录下的文件:

使用 unzip 解压可以控制只解压该目录下的文件:
1 | unzip commandline-tools-linux-x64-5.0.13.230.zip "command-line-tools/sdk/default/openharmony/native/*" |
接下来我们要配置环境变量,让项目工程可以识别使用鸿蒙的工具链进行初始化工程及编译,假设你的工具链目录解压到了 ~/Downloads/command-line-tools 目录下:
设置当前终端环境变量(不污染整个系统)
1 | export CC=~/Downloads/command-line-tools/sdk/default/openharmony/native/llvm/bin/clang |
此时如果你在当前终端编译一个没有三方库依赖的代码,就已经可以使用鸿蒙的工具链了,我使用了一个简单的 CMake 项目做了测试:
1 | $ cmake -Bbuild -DCMAKE_C_FLAGS="--target=aarch64-linux-ohos -D__MUSL__=1" -DCMAKE_CXX_FLAGS="--target=aarch64-linux-ohos -D__MUSL__=1" |
可以看到,C compiler 和 CXX compiler 都已经使用我们之前设置的鸿蒙的工具链了,其中 CMAKE_C_FLAGS 和 CMAKE_CXX_FLAGS 携带的两个参数分别表示:
--target=aarch64-linux-ohos
明确使用 aarch64-linux-ohos 工具链-D__MUSL__=1
添加全局宏明确表示使用 musl libc 而不是 GNU libc,这是华为官网帮助文档中要求加入的
执行 cmake 的编译指令后,即可编译出产物:
1 | $ cmake --build build --config Release |
使用 file 命令查看一下产物,也已经是 aarch64 架构了:
1 | $ file build/libopenharmony-library.so |
如果没有以上两个参数,会无法正确初始化 CMake 工程报错,如:
1 | $ cmake -Bbuild |
添加鸿蒙平台的 conan profile
如果想让 conan 也使用这套配置,我们还需要新增一个针对鸿蒙工具链的 profile 配置文件:
1 | [settings] |
参数解释:
os=Linux
表示让产物还是以 Linux like 方式编译,因为鸿蒙的交叉编译工具链还是基于 Linux like 改进arch=armv8
编译为 armv8 架构的,这个参数还是要给,虽然重要性不大,因为我们在工具链中设置了 —target 参数compiler=clang
表示使用 clang,conan 会做校验compiler.version=15
表示使用 clang 15 版本,版本号是我从鸿蒙工具链中获取的,如果这里给的不对 conan 会给警告compiler.libcxx=c++_static
表示静态链接 C++ 标准库,这个类似 NDK,给 c++_shared 表示动态链接系统库compiler.cppstd=11
表示如果被编译目标是 C++ 代码,则以 C++ 11 作为标准编译[env]
中所有参数就是工具链的目录,其中比较重要的是 CFLAGS 和 CXXFLAGS 明确使用哪种架构和添加全局宏
将以上内容保存为名为 openharmony-arm64-v8a-clang15
的文件,然后尝试使用 conan 编译一个三方库 openssl 1.1.1w 的版本,执行命令后输出如下:
1 | $ conan install openssl/1.1.1w@ -pr:b=default -pr:h=./openharmony-arm64-v8a-clang15 --build missing |
可以看到编译 openssl 已经能正确识别到我们通过 conan profile 设置的工具链,命令相关参数解释:
-pr:b=default
如果在交叉编译中需要运行一下本地程序来对代码进行更改如应用 patch 等,需要使用本地环境而不是编译的目标环境,系统默认是 gcc-pr:h=./openharmony-arm64-v8a-clang15
表示编译的目标环境使用这个配置--build missing
表示本地如果没有或有依赖不存则,则直接重新编译
说白了这几个参数就是告诉 conan 需要从本地的 x86_64 环境交叉编译到鸿蒙的 arm64-v8a,同时给出了本地环境的工具链和目标环境的工具链路径、参数配置等信息。如果想进一步验证配置有效性,我建议使用 libcurl 进行测试,因为 libcurl 在编译目标产物时还需要在本地执行 m4、automake 等工具来生成一些编译配置,而且完整的 libcurl 编译也会自动依赖 openssl、zlib 等三方库。如果 libcurl 也可以通过,那么你的环境配置就没什么问题了,命令如下:
1 | conan install libcurl/8.6.0@ -pr:b=default -pr:h=./openharmony-arm64-v8a-clang15 -s build_type=Release --build missing |
多了个 -s build_type=Release 参数主要是一些三方库要明确指定编译的类型,如 zlib
工程化集成
为了方便 CI 集成,我们将以上步骤封装为一个携带了 conan + 鸿蒙工具链的 Dockerfile,如果你需要快速创建一个鸿蒙的交叉编译环境,可以考虑使用:
1 | FROM conanio/gcc9-ubuntu18.04:latest |
编译镜像需要至少指定一个 TOOLCHAIN_URL 的参数,该参数是鸿蒙交叉编译工具链的下载地址,因为华为官方的地址需要鉴权,所以我们下载后备份到了自己的服务器上,您也可以上传到自己的内网服务器上来测试用:
1 | docker build -t openharmony-toolchain . --build-arg \ |
编译完成后可以直接基于镜像运行容器:
1 | docker run -it \ |
运行后就进入了包含 conan 和鸿蒙交叉编译工具链的环境了,您可以直接在该环境下编译鸿蒙的项目,您可以在镜像中集成 jenkins agent 或 gitlab runner 作为一个 CI 环境直接使用。
已知问题
在编译业务代码时,我们碰到了一些小问题,如链接不到 pthread_cancel
函数,虽然编译能过,但是链接失败,这是因为 musl 没有导出该方法,推荐使用 pthread_kill
实现,在业务代码中,可通过宏 __OHOS__
来判断是否是使用鸿蒙工具链编译的产物,如:
1 |
|
另外因为业务代码有依赖 libuv,在使用 conan 依赖 libuv 时同样会出现链接 pthread_setaffinity_np
失败的情况,你可以切换到直接链接系统的 libuv 而不是使用 conan 依赖,鸿蒙工具链中携带了预编译好的 uv 库,还能进一步减少产物体积。
如果你就是不想用系统的 libuv,希望独立管理 libuv 代码,你需要关注一下 libuv 1.51.x 版本针对 OHOS 的适配:
1 |
|
更重要的是,libuv 官方代码即使你设置了 -fvisibility=hidden
选项,其代码还是会默认导出 API,这会与系统默认携带的 libuv 冲突,运行时挂掉,这是我碰到的崩溃栈:
1 | Module name:com.netease.nimsample |
你需要修改 libuv 的代码,根据配置决定是否导出符号,可以参考这个 PR(未合入官方代码):https://github.com/libuv/libuv/pull/4144