在 OpenHarmony 应用开发中选择 Native C ++开发方式需要使用 OpenHarmony NDK 工具,或者想移植一个 C/C ++的三方库到 OpenHarmony 中也可以使用 NDK。NDK 使用到的编译工具是 cmake,也就是 CMakeLists.txt 那一套。Native C ++应用开发方式可以做直接编译三方库的 so 库再利用 NAPI 框架实现能力的调用(只需要在三方库原生的 CMakeLists.txt 中修改小部分的内容就可以实现)。
一、OpenHarmony NDK
NDK (原生开发套件) 是一套工具,使开发者能够在 OpenHarmony hap 应用中使用 C/C++ 代码。
NDK 提供了一系列的工具可以帮助开发者快速的开发 C/C++的动态库、静态库和可执行文件。
OpenHarmony 应用开发的Native C++开发方式就要依赖 NDK。NDK 被包含在 OpenHarmony SDK 中。可以在DevEco Studio使用 NDK 将 C/C ++ 代码编译到 so 库中,然后使用 DevEco Studio 的构建插件hvigor-ohos-plugin将 so 库打包到 Hap 中。ArkTS 代码随后可以通过NAPI框架调用 SO 库中的函数。
二、获取 NDK 的方式
2.1 从每日构建中获取
每日构建地址: http://ci.openharmony.cn/dailys/dailybuilds
组件形态选择ohos-sdk
,版本选择最新版本的 sdk 一般是没有问题的(但是笔者之前遇到下载的 ndk 中的clang工具找不到libatomic.so无法工作的情况的情况)
下载解压后可以得到如下文件,根据 linux 或者 windows 端解压不同文件得到 sdk
2.2 编译源码得到 SDK 中的 NDK
安装依赖
./build/build_scripts/env_setup.sh
执行完上述命令后记得执行source ~/.bashrc或者重启终端
source ~/.bashrc
安装编译SDK需要的依赖包(编译镜像的时候是不依赖这些包的)
sudo apt-get install libxcursor-dev libxrandr-dev libxinerama-dev
./build.sh --product-name ohos-sdk --ccache --build-target ohos_ndk
复制代码
写文章提的 issue https://gitee.com/openharmony/build/issues/I6H8IO?from=project-issue
二、NDK 目录
native
├── NOTICE.txt 声明文件
├── build
│ └── cmake
│ ├── ohos.toolchain.cmake ---->编译的工具链
│ └── sdk_native_platforms.cmake ---->编译的工具链(在DevEco Studio中编译打包so要用到)
├── build-tools ---->cmake编译工具所在目录(NDK提供的编译工具)
├── llvm ---->编译器所在目录(NDK提供的编译工具)
├── sysroot ---->编译器的 sysroot 目录,存放 SDK 内部的已经包含的库和对应的头文件
├── ndk_system_capability.json ---->NDK自带so库能力描述文件
├── nativeapi_syscap_config.json ---->调用NDK自带so库能力相关头文件
├── docs ---->调用NDK自带so库能力相关文档
└── oh-uni-package.json ---->SDK 信息描述
复制代码
FileCheck clang-format ld64.lld lldb-vscode llvm-cxxfilt llvm-objdump llvm-strip yaml2obj
clang clang-tidy lld llvm-addr2line llvm-dis llvm-profdata llvm-symbolizer
clang++ clangd lld-link llvm-ar llvm-lib llvm-ranlib not
clang-12 count lldb llvm-as llvm-link llvm-readelf sancov
clang-check dsymutil lldb-argdumper llvm-cfi-verify llvm-modextract llvm-readobj sanstats
clang-cl git-clang-format lldb-mi llvm-config llvm-nm llvm-size scan-build
clang-cpp ld.lld lldb-server llvm-cov llvm-objcopy llvm-strings scan-view
复制代码
三、linux 下使用 NDK 编译库文件和可执行文件
3.1 使用 NDK 编译一个简单 demo
shared-library
├── CMakeLists.txt 外部CMakeLists.txt
├── include 头文件目录
│ └── shared
│ └── Hello.h
└── src 源文件目录
├── CMakeLists.txt 内部CMakeLists.txt
├── Hello.cpp
└── main.cpp
复制代码
#cmake的版本
CMAKE_MINIMUM_REQUIRED(VERSION 3.16)
#工程名称
PROJECT(HELLO_LIBRARY)
#添加一个子目录并构建该子目录
ADD_SUBDIRECTORY(src)
复制代码
cmake 的内置命令是不区分大小写的,因此 add_subdirectory 与 ADD_SUBDIRECTORY 作用一致。但是 cmake 的所有变量都是区分大小写的
#设置可执行文件输出路径
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/output)
# 设置so库文件输出路径
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/output)
############################################################
# Create a library
############################################################
#SHARED表示生成的是动态库,动态库的名字是hello_shared_library。生成动态库的源文件是Hello.cpp
ADD_LIBRARY(hello_shared_library SHARED
Hello.cpp
)
#为生成的动态库添加一个别名,后续hello::library可使用来替代hello_shared_library
ADD_LIBRARY(hello::library ALIAS hello_shared_library)
#为指定目标hello_shared_library添加头文件搜索路径(这个指定目标决不能是alias target,也就是指定的别名hello::library)
TARGET_INCLUDE_DIRECTORIES(hello_shared_library
PUBLIC
${PROJECT_SOURCE_DIR}/include
)
############################################################
# Create an executable
############################################################
# 使用指定的源文件main.cpp创建出一个可执行文件hello_shared_binary
ADD_EXECUTABLE(hello_shared_binary
main.cpp
)
#TARGET_LINK_LIBRARIES指令的作用为将目标文件与库文件进行链接
#将可执行文件hello_shared_binary与库文件hello::library进行链接
TARGET_LINK_LIBRARIES( hello_shared_binary
PRIVATE
hello::library
)
复制代码
3.2 进行 cmake 编译
3.2.1 NDK 自带的 cmake 编译工具添加到环境变量
1、第一种方法:
#将其临时加入环境变量中(适用于临时想用用NDK)
export PATH=$PATH:/ohos-sdk/linux/native/build-tools/cmake/bin(你自己解压的NDK目录)
复制代码
2、第二种方法:
#将其永远加入环境变量中
#打开.bashrc文件
vim ~/.bashrc
#在文件最后添加cmake路径,该路径是自己的放置文件的路径,之后保存退出
export PATH=/ohos-sdk/linux/native/build-tools/cmake/bin:$PATH
#在命令行执行source ~/.bashrc使环境变量生效
source ~/.bashrc
复制代码
3、查看环境变量添加是否成功
可知 cmake 的版本是 3.16.5 ,所以编写 CMakeLists.txt 可参考的链接为 https://cmake.org/cmake/help/v3.16/guide/tutorial/
3.2.2 安装 make
3.2.3 cmake 的几个参数
1、OHOS_STL
参数,其类型可以是c++_shared
和c++_static
,默认是c++_shared
2、OHOS_ARCH
参数(设置应用程序二进制接口 ABI),其类型是armeabi-v7a
、x86_64
,默认值是arm64-v8a
。每一种 ABI 定义了相应的二进制文件。
armeabi-v7a
(选择这个是编译 32 位的二进制文件)
arm64-v8a
(选择这个是编译 64 位的二进制文件)
x86_64
(现在不使用,OpenHarmony 现在在适配 x86 的芯片)
3、OHOS_PLATFORM
参数,其类型为OHOS
(只能选择 OHOS 平台)
4、 CMAKE_TOOLCHAIN_FILE
参数指向的是工具链文件所在的位置,就是ohos.toolchain.cmake
的路径(对于编译不同平台二进制文件是很重要的)。
# Common default settings
39:if(NOT DEFINED OHOS_PLATFORM_LEVEL)
set(OHOS_PLATFORM_LEVEL 1)
endif()
43:if(NOT DEFINED OHOS_TOOLCHAIN)
set(OHOS_TOOLCHAIN clang)
endif()
47:if(NOT DEFINED OHOS_STL)
set(OHOS_STL c++_shared)
endif()
51:if(NOT DEFINED OHOS_PIE)
set(OHOS_PIE TRUE)
endif()
55:if(NOT DEFINED OHOS_ARM_NEON)
set(OHOS_ARM_NEON thumb)
endif()
60:if(NOT DEFINED OHOS_ARCH)
set(OHOS_ARCH arm64-v8a)
endif()
复制代码
3.2.4 使用 CMake 进行构建,并传递工具链文件及 cmake 参数
# dmeo目录下创建build目录,用来放置cmake构建时产生的中间文件。
mkdir build && cd build
# 传递OHOS_STL(可选,默认就是c++_shared)、OHOS_ARCH、OHOS_PLATFORM、工具链文件
# CMAKE_TOOLCHAIN_FILE是ohos.toolchain.cmake具体放置的路径
# 这一步会检查CMakeLists.txt是否有语法错误
# 不要忘记在ohos.toolchain.cmake路径后面加上 ..
cmake -D OHOS_STL=c++_shared -D OHOS_ARCH=armeabi-v7a -D OHOS_PLATFORM=OHOS -D CMAKE_TOOLCHAIN_FILE=/xxx/build/cmake/ohos.toolchain.cmake ..
cmake --build .
复制代码
1、执行如下过程如下:
2、可以注意到执行cmake --build .
时会有 warning: -Wunused-command-line-argument
,通过在内部的 CMakeLists.txt 中添加如下语句消除 warning。
set(CMAKE_CXX_FLAGS "-Wno-unused-command-line-argument")
复制代码
3、编译结果如下:生成可执行文件和动态库
4、要编译静态库及其可执行文件,内部的 CMakeLists.txt 中添加如下语句
############################################################
# Create a library
############################################################
#Generate the static library from the library sources
ADD_LIBRARY(hello_static_library STATIC
Hello.cpp
)
TARGET_INCLUDE_DIRECTORIES(hello_static_library
PUBLIC
${PROJECT_SOURCE_DIR}/include
)
############################################################
# Create an executable
############################################################
# Add an executable with the above sources
ADD_EXECUTABLE(hello_static_binary
main.cpp
)
# link the new hello_library target with the hello_binary target
TARGET_LINK_LIBRARIES( hello_static_binary
PRIVATE
hello_static_library
)
复制代码
编译结果如下,生成静态库及其可执行文件
5、编译 64 位的库文件和可执行文件,OHOS_ARCH 参数选择 arm64-v8a 或者编译时不传递该参数。
3.4.5 使用 CMake 进行构建,选择 ninja 生成器并生成编译流程图
1、执行cmake -G
查看 linux 环境下的生成器,ndk 中自带的生成器是 Ninja。
2、使用 CMake 进行构建时添加上-G "Ninja"
第一步:mkdir build && cd build
第二步:cmake -G "Ninja" -D OHOS_STL=c++_shared -D OHOS_ARCH=armeabi-v7a -D OHOS_PLATFORM=OHOS -D CMAKE_TOOLCHAIN_FILE=/xxx/build/cmake/ohos.toolchain.cmake ..
第三步:cmake --build .或者ninja -f build.ninja
复制代码
执行完第二步后,build 目录下会生成 build.ninja,有了 build.ninja 可以使用 ninja 生成程序的编译流程图。
3、使用 ninja 工具生成库文件和可执行文件的编译流程图。
# 将libhello_shared_library.so动态库的编译流程转为dot
ninja -t graph libhello_shared_library.so > xxx.dot
# 将dot格式转化为png格式的流程图
dot -T png xxx.dot -o libhello_shared_library.so.png
# 将dot格式转化为svg格式的流程图
dot -T svg xxx.dot -o libhello_shared_library.so.svg
复制代码
如果生成图片格式太小的话,可以生成 svg 格式到浏览器打开,这一点很重要,请参考该 issue
3.4.6 使用 NDK 中的 cmake-gui 进行图形化操作编译
1、native/build-tools/cmake/bin 目录有 cmake-gui,可以用它图形化传入编译参数进行编译。
2、首先点击Where is source code
行的Browser Source
,加载工程所在目录。再点击Where to build the binarys
行的Browser Source
,加载点击“Where is source code”行的 Browser Source,加载源码所在目录。点击“Where to build the binarys 行的 Browser Source,加载工程所在目录下的 build 目录(没有就创建一个)。
3、点击Add Entry
传入 cmake 参数。勾选把旁边的Grouped
选项。
4、点击Configure
选择Current Generator
生成器为Unix Makefiles
。
5、最后打开Where to build the binarys
加载构建目录终端,执行make -j 8
命令
make -j n 含义是 让 make 最多允许 n 个编译命令同时执行,这样可以更有效的利用 CPU 资源。假设系统 cpu 是 12 核,在不影响其他工作的情况下,我们可以 make -j 12 将 cpu 资源充分利用起来,一般来说,最大并行任务数为 cpu_num * 2。
写文章提的 issue https://gitee.com/openharmony/third_party_musl/issues/I6JRBU?from=project-issue
评论