Windows下CMake通过鸿蒙SDK交叉编译三方库
前言
华为鸿蒙官方的文章CMake构建工程配置HarmonyOS编译工具链 中介绍了在Linux平台下如何使用CMake来配置鸿蒙的交叉编译环境,编译输出在Harmony中使用的第三方so库以及测试demo。
本文主要是在Windows下实现同样的操作。由于平台差异的原因,有些细节的配置会有不同。
CMake简介
CMake是一个跨平台的构建工具,用于管理构建过程、编译、链接和打包软件项目,它可以生成Makefile等用于不同操作系统和编译器的构建脚本。CMake的配置过程是跨平台的,因此可以在不同的操作系统上运行,例如Linux、Windows和macOS。
CMake构建过程可分为以下三个主要步骤:
- 配置(Configuration):配置阶段是CMake解析CMakeLists.txt文件的过程。在配置阶段,CMake会读取CMakeLists.txt文件,并执行其中的命令。CMakeLists.txt文件是CMake的核心,其定义了项目的构建规则和依赖关系。
- 生成(Generation):生成阶段是CMake根据配置阶段的结果,生成实际构建文件的过程。在生成阶段,CMake会将CMakeLists.txt文件中定义的构建规则和依赖关系转为构建工具可以理解的形式。
- 构建(Build):构建阶段是使用构建工具(如Make或DevEco Studio)根据生成的构建文件,编译源代码并链接生成目标文件的过程。在构建阶段,构建工具会读取生成的构建文件,按照其中定义的规则和依赖关系,执行实际的编译和链接操作。
cJSON源码准备
同样, 我们这里也使用开源的三方库cJSON进行编译。
源码地址在这里
可以使用git 克隆到本地,或者直接下载源码压缩包到本地,解压到本地
环境准备
这里我使用的是上篇博文中使用的HarmonyOS SDK V4.1.7版本进行编译,注意,SDK文件夹存放的路径名称中一定不能包含空格。我是直接存放在D盘根目录,如下:
然后还需要准备mingw编译器,在如果是Qt开发者,安装Qt的时候可以选择直接安装mingw工具,如果没有的话,可以自行下载安装一个:地址
开始编译
我自己在编译过程中遇到很多问题,踩了挺多坑的,为了方便理解,当前博文中也一步步的从头开始,将遇到的错误问题全部抛出来。
进入到cJSON源码目录,创建一个编译的目录 build
,然后进入到build目录, 鼠标右键->在终端中打开:
或者通过cmd终端然后进入到build目录。
然后在终端中输入命令:
D:/HarmonyOS-4.1.7/11/native/build-tools/cmake/bin/cmake.exe -DCMAKE_TOOLCHAIN_FILE=D:/HarmonyOS-4.1.7/11/native/build/cmake/ohos.toolchain.cmake -DCMAKE_C_COMPILER=D:/HarmonyOS-4.1.7/11/native/llvm/bin/clang.exe -DCMAKE_CXX_COMPILER=D:/HarmonyOS-4.1.7/11/native/llvm/bin/clang++.exe -DOHOS_ARCH=arm64-v8a ..
以下是分段展示,方便查看:
D:/HarmonyOS-4.1.7/11/native/build-tools/cmake/bin/cmake.exe
-DCMAKE_TOOLCHAIN_FILE=D:/HarmonyOS-4.1.7/11/native/build/cmake/ohos.toolchain.cmake
-DCMAKE_C_COMPILER=D:/HarmonyOS-4.1.7/11/native/llvm/bin/clang.exe
-DCMAKE_CXX_COMPILER=D:/HarmonyOS-4.1.7/11/native/llvm/bin/clang++.exe
-DOHOS_ARCH=arm64-v8a …
说明:
我这里使用的是HarmonyOS SDK目录下的cmake,由于我没有设置环境变量,所以都是用的绝对路径。
- -DCMAKE_TOOLCHAIN_FILE:交叉编译配置文件路径,设置为ohos工具链配置文件
- -DCMAKE_C_COMPILER:配置鸿蒙SDK的C编译器
- -DCMAKE_CXX_COMPILER:配置鸿蒙SDK的C++编译器
- -DOHOS_ARCH:配置交叉编译的CPU架构,一般为arm64-v8a(编译64位的三方库)或armeabi-v7a(编译32位的三方库),本示例中设置编译为64位的cJSON库
执行命令后,报错:
PS F:\OpenHarmony\QTHarmony\Demo\cJSON-master\cJSON-master\build> D:/HarmonyOS-4.1.7/11/native/build-tools/cmake/bin/cmake.exe -DCMAKE_TOOLCHAIN_FILE=D:/HarmonyOS-4.1.7/11/native/build/cmake/ohos.toolchain.cmake -DCMAKE_C_COMPILER=D:/HarmonyOS-4.1.7/11/native/llvm/bin/clang.exe -DCMAKE_CXX_COMPILER=D:/HarmonyOS-4.1.7/11/native/llvm/bin/clang++.exe -DOHOS_ARCH=arm64-v8a ..
-- Building for: Visual Studio 16 2019
-- The C compiler identification is MSVC 19.29.30038.1
-- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.29.30037/bin/Hostx64/x64/cl.exe
-- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.29.30037/bin/Hostx64/x64/cl.exe -- broken
CMake Error at D:/HarmonyOS-4.1.7/11/native/build-tools/cmake/share/cmake-3.16/Modules/CMakeTestCCompiler.cmake:60 (message):The C compiler"C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.29.30037/bin/Hostx64/x64/cl.exe"is not able to compile a simple test program.It fails with the following output:Change Dir: F:/OpenHarmony/QTHarmony/Demo/cJSON-master/cJSON-master/build/CMakeFiles/CMakeTmpRun Build Command(s):C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/MSBuild/Current/Bin/MSBuild.exe cmTC_22cc6.vcxproj /p:Configuration=Debug /p:Platform=x64 /p:VisualStudioVersion=16.0 /v:m && 用于 .NET Framework 的 Microsoft (R) 生成引擎版本 16.10.2+857e5a733版权所有(C) Microsoft Corporation。保留所有权利。用于 x64 的 Microsoft (R) C/C++ 优化编译器 19.29.30038.1 版版权所有(C) Microsoft Corporation。保留所有权利。cl /c /W1 /WX- /diagnostics:column /O2 /D __MUSL__ /D "CMAKE_INTDIR=\"Debug\"" /D _MBCS /Gm- /MD /GS /fp:precise /Zc:wchar_t /Zc:forScope /Zc:inline /Fo"cmTC_22cc6.dir\Debug\\" /Fd"cmTC_22cc6.dir\Debug\vc142.pdb" /external:env:EXTERNAL_INCLUDE /external:W1 /Gd /TC /errorReport:queue -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -fno-addrsig -Wa,--noexecstack -Wformat -Werror=format-security -O0 -g -fno-limit-debug-info "F:\OpenHarmony\QTHarmony\Demo\cJSON-master\cJSON-master\build\CMakeFiles\CMakeTmp\testCCompiler.c"cl : 命令行 error D8021: 无效的数值参数“/Wa,--noexecstack” [F:\OpenHarmony\QTHarmony\Demo\cJSON-master\cJSON-master\build\CMakeFiles\CMakeTmp\cmTC_22cc6.vcxproj]CMake will not be able to correctly generate this project.
Call Stack (most recent call first):CMakeLists.txt:4 (project)
-- Configuring incomplete, errors occurred!
错误原因是由于在Windows下CMake默认在Windows上选择Visual Studio生成器,而非鸿蒙NDK所需的Ninja或MinGW生成器,并且-Wa,–noexecstack是Clang/Linux平台的参数,MSVC无法识别。
指定MinGW Makefiles作为生成器
所以需要在cmake命令中强制使用鸿蒙工具链的生成器,在这里我们先使用mingw作为生成器。
修改cmake命令,如下:
D:/HarmonyOS-4.1.7/11/native/build-tools/cmake/bin/cmake.exe -G "MinGW Makefiles" -DCMAKE_TOOLCHAIN_FILE=D:/HarmonyOS-4.1.7/11/native/build/cmake/ohos.toolchain.cmake -DCMAKE_C_COMPILER=D:/HarmonyOS-4.1.7/11/native/llvm/bin/clang.exe -DCMAKE_CXX_COMPILER=D:/HarmonyOS-4.1.7/11/native/llvm/bin/clang++.exe -DCMAKE_MAKE_PROGRAM=D:/Qt/Tools/mingw810_64/bin/mingw32-make.exe -DOHOS_ARCH=arm64-v8a ..
分段展示如下:
D:/HarmonyOS-4.1.7/11/native/build-tools/cmake/bin/cmake.exe -G “MinGW Makefiles”
-DCMAKE_TOOLCHAIN_FILE=D:/HarmonyOS-4.1.7/11/native/build/cmake/ohos.toolchain.cmake
-DCMAKE_C_COMPILER=D:/HarmonyOS-4.1.7/11/native/llvm/bin/clang.exe
-DCMAKE_CXX_COMPILER=D:/HarmonyOS-4.1.7/11/native/llvm/bin/clang++.exe
-DCMAKE_MAKE_PROGRAM=D:/Qt/Tools/mingw810_64/bin/mingw32-make.exe
-DOHOS_ARCH=arm64-v8a …
以上cmake命令中添加了 -G "MinGW Makefiles"
,显式指定了mingw作为生成器,并且设置-DCMAKE_MAKE_PROGRAM
指定了mingw编译器的路径。
清理build目录下上一次失败运行生成缓存文件,然后再次运行:
这时候就顺利执行成功了:
然后再执行编译命令,编译Release版本:
cmake --build . --config Release
可以编译成功了:
在build目录就可以看到编译生成的so文件了:
至此,本文的目标 编译任务已经完成。
前面提到,还可以指定Ninja
作为生成器,并且跟mingw有差异,那接下来感兴趣的可以继续一起来看看使用Ninja会遇到的问题。
使用Ninja作为生成器
接下来我们使用Ninja
作为生成器,修改cmake命令如下:
D:/HarmonyOS-4.1.7/11/native/build-tools/cmake/bin/cmake.exe -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=D:/HarmonyOS-4.1.7/11/native/build/cmake/ohos.toolchain.cmake -DCMAKE_C_COMPILER=D:/HarmonyOS-4.1.7/11/native/llvm/bin/clang.exe -DCMAKE_CXX_COMPILER=D:/HarmonyOS-4.1.7/11/native/llvm/bin/clang++.exe -DCMAKE_MAKE_PROGRAM=D:/Qt/Tools/Ninja/ninja.exe -DOHOS_ARCH=arm64-v8a ..
分段展示如下 方便查看:
D:/HarmonyOS-4.1.7/11/native/build-tools/cmake/bin/cmake.exe -G “Ninja”
-DCMAKE_TOOLCHAIN_FILE=D:/HarmonyOS-4.1.7/11/native/build/cmake/ohos.toolchain.cmake
-DCMAKE_C_COMPILER=D:/HarmonyOS-4.1.7/11/native/llvm/bin/clang.exe
-DCMAKE_CXX_COMPILER=D:/HarmonyOS-4.1.7/11/native/llvm/bin/clang++.exe
-DCMAKE_MAKE_PROGRAM=D:/Qt/Tools/Ninja/ninja.exe
-DOHOS_ARCH=arm64-v8a …
说明:
以上命令中显式指定了 -G “Ninja”,并且通过 -DCMAKE_MAKE_PROGRAM
指定了ninja.exe的安装路径。
执行命令后又报错,信息如下:
-- Performing Test FLAG_SUPPORTED_fvisibilityhidden
-- Performing Test FLAG_SUPPORTED_fvisibilityhidden - Success
-- Configuring done
CMake Error at CMakeLists.txt:130 (add_library):The install of the cjson target requires changing an RPATH from the buildtree, but this is not supported with the Ninja generator unless on anELF-based platform. The CMAKE_BUILD_WITH_INSTALL_RPATH variable may be setto avoid this relinking step.CMake Error at CMakeLists.txt:130 (add_library):The install of the cjson target requires changing an RPATH from the buildtree, but this is not supported with the Ninja generator unless on anELF-based platform. The CMAKE_BUILD_WITH_INSTALL_RPATH variable may be setto avoid this relinking step.
问题原因:
1.RPATH 冲突
CMake 提示 The install of the cjson target requires changing an RPATH from the build tree…,表明在安装(make install)时需要修改构建目录中的RPATH(运行时库搜索路径)。但 Ninja 在 Windows 上不支持修改非 Windows 原生二进制(如 Linux/鸿蒙的ELF 格式)的 RPATH
2.工具链配置冲突
鸿蒙的 ohos.toolchain.cmake
可能默认启用了CMAKE_BUILD_WITH_INSTALL_RPATH(要求构建时使用安装后的 RPATH),而 Windows 上的
Ninja 无法处理此操作
解决方案是,禁用RPATH,在cmake命令中新增参数:
-DCMAKE_SKIP_RPATH=ON \
-DCMAKE_SKIP_INSTALL_RPATH=ON \
-DCMAKE_BUILD_WITH_INSTALL_RPATH=OFF
完整命令如下:
D:/HarmonyOS-4.1.7/11/native/build-tools/cmake/bin/cmake.exe -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=D:/HarmonyOS-4.1.7/11/native/build/cmake/ohos.toolchain.cmake -DCMAKE_C_COMPILER=D:/HarmonyOS-4.1.7/11/native/llvm/bin/clang.exe -DCMAKE_CXX_COMPILER=D:/HarmonyOS-4.1.7/11/native/llvm/bin/clang++.exe -DCMAKE_MAKE_PROGRAM=D:/Qt/Tools/Ninja/ninja.exe -DOHOS_ARCH=arm64-v8a -DCMAKE_SKIP_RPATH=ON -DCMAKE_SKIP_INSTALL_RPATH=ON -DCMAKE_BUILD_WITH_INSTALL_RPATH=OFF ..
分段展示如下:
D:/HarmonyOS-4.1.7/11/native/build-tools/cmake/bin/cmake.exe -G “Ninja”
-DCMAKE_TOOLCHAIN_FILE=D:/HarmonyOS-4.1.7/11/native/build/cmake/ohos.toolchain.cmake
-DCMAKE_C_COMPILER=D:/HarmonyOS-4.1.7/11/native/llvm/bin/clang.exe
-DCMAKE_CXX_COMPILER=D:/HarmonyOS-4.1.7/11/native/llvm/bin/clang++.exe
-DCMAKE_MAKE_PROGRAM=D:/Qt/Tools/Ninja/ninja.exe -DOHOS_ARCH=arm64-v8a
-DCMAKE_SKIP_RPATH=ON
-DCMAKE_SKIP_INSTALL_RPATH=ON
-DCMAKE_BUILD_WITH_INSTALL_RPATH=OFF …
再次执行命令,可以顺利通过,然后再执行编译命令:cmake --build . --config Release
即可完成。
技术延伸
1.RPATH 是什么:
ELF 格式(Linux/鸿蒙)中嵌入的动态库搜索路径,Windows 的 PE 格式不支持此机制。
2.Ninja 的限制:
在非 ELF 主机平台(如 Windows)编译 ELF 目标时,无法处理跨格式的 RPATH 重定向;
3.鸿蒙的特殊性:
目标平台为 ARM64 ELF,但主机是 Windows,工具链需交叉编译,RPATH 操作需在鸿蒙设备运行时解析(无需在构建时处理)。
以上步骤中,使用Ninja生成器,我们禁用了RPATH,那么禁用过后编译出来的库会有什么问题吗?
1. 运行时动态库查找机制失效
- RPATH 的作用:RPATH 是嵌入在 ELF 文件(鸿蒙的动态库格式)中的路径信息,用于指导动态链接器(如鸿蒙的 ld-musl.so)在运行时查找依赖库的位置。
- 禁用后的行为:
编译生成的库文件(如 libcjson.so)将不再包含任何自定义的运行时库搜索路径。若该库依赖其他三方库(例如 libz.so),且这些库未放置在鸿蒙系统的默认库路径(如 /system/lib)中,则运行时会出现 dlopen failed: library not found 错误。
2. 依赖库部署灵活性降低
若你的库是独立的三方库(如 libcjson.so),且不依赖其他外部库,则禁用 RPATH 无影响。
若你的库依赖其他非系统库(如自行编译的 libfoo.so),则需手动将这些依赖库部署到鸿蒙设备的系统路径,或通过环境变量指定路径,否则无法运行
。
3. 与鸿蒙动态共享包(HSP)机制的兼容性
鸿蒙的 HSP(动态共享包)要求库文件能通过相对路径加载依赖(例如 $ORIGIN/…/lib)
。禁用 RPATH 后,此机制失效,需依赖鸿蒙的应用内 HSP 部署规范(如固定目录结构)解决。
为什么 MinGW 无需禁用 RPATH?
1.格式兼容:MinGW 的 GCC 工具链原生支持生成和处理 ELF 格式文件,而 RPATH 是 ELF 的核心特性之一;
2.工具链一致性:MinGW 的链接器(ld)与 Linux 环境一致,能直接解析 RPATH 指令,无需格式转换或兼容层;
3.交叉编译友好:作为跨平台编译器,MinGW 在设计上已考虑非 Windows 目标(如鸿蒙)的编译需求,而 MSVC 仅聚焦 Windows 平台;