以 jrtplib 的依赖库 jthread 的编译为例, 介绍 cmake 的交叉编译, 因为这个库相对简单, 构建完成后就可以直接使用, 将动态库或者静态库放置到安卓指定的文件夹下, 通过引用头文件来编写 jni 即可
这篇文章用到了一点 cmake 交叉编译的东西, 在我的掘金主页有关于如何使用 cmake 的教程
juejin.im/post/5a8ab0
文章废话少, 干货多, 请静下心来看:
关于 ndk 的东西网上已经烂大街了, 我这里就不多介绍了这篇文章主要是关于如何使用 bash 控制 cmake 来 cross-compile
创建 build 文件夹用于外部构建
创建 target 及架构平台文件夹用于存放构建好的动态库
cmake 外部编译交叉编译
将构建好的项目导出至 target 文件夹下
ndk-build 生成多 abi 动态库相对简单, 安卓环境的 cmake 环境生成动态库也很简单, 不多说, 但是在非安卓 cmake 环境中的 cmake 中我目前还没找到如何一次生成多个平台架构, 所以写了一个脚本来实现
由于脚本相对来说比较简单, 这文章篇幅不会很长:
android 架构
armeabi
armeabiv7a
arm64v8a
- x86
- x86_64
- mips
- mips64
这七种是我们今天将要编译的 abi, 这七个文件夹会在 target 文件夹中生成, 并在对应的文件夹下生成动态库
一些变量
使用 cmake 交叉编译时必须找到 ndk 路径, 否则不能编译, 定义如下, 将 NDK_PATH 替换为自己的路径即可
- #!/bin/bash
- #ndk 的路径, 替换为自己的路径
- export NDK_PATH=/Users/rangaofei/Library/Android/sdk/ndk-bundle
- # 将要构建的架构
- TARGETS=(armeabi arm64-v8a armeabi-v7a mips mips64 x86 x86_64)
准备 build 文件夹
采用外部构建的方式, build 文件夹是构建的文件夹, 所有的生成文件都会在这边, 所以先检测是否有这个文件夹, 没有就创建, 有就清除里边的内容
- #清除build文件夹下的内容
- function clean_build() {
- if ([ - d build]);
- then echo "prepare to clean cache" (rm - rf. / build
- /*)
- echo "complete"
- else
- echo "build is not a directory"
- exit 0
- fi
- }
- function prepare_build() {
- # 检测是否有 Build 文件夹, 有的话删除文件夹, 没有的话创建文件夹
- if ([ -e build ]); then
- echo "you already have build dir"
- clean_build
- else
- echo "prepare to create dir build"
- mkdir build
- fi
- }*/
准备 target 文件夹
target 文件夹是我们用来存放各种 abi 动态库的文件夹, 我们会将 build 文件夹中生成的动态库和静态库复制到 target 对应的文件夹下
- function prepare_target() {#检测是否有所有的target文件夹,
- 有则删除,
- 没有则创建
- if ([ - e target] && [ - d target]);
- then echo "prepare to clean target"rm - rf. / target
- /*
- echo "clean target complete"
- else
- echo "you not have target_dir,we will create it"
- mkdir target
- fi
- }
- function create_child_dir() {
- if ([ -e target ]); then
- (
- cd target
- mkdir $1
- )
- else
- echo "target is not a dir"
- fi
- }
- function move_to_target() {
- if ([ -e ./build/src/libjthread.a ] && [ -e ./build/src/libjthread.so ]); then
- echo "prepare move target to ./target/$1"
- mv ./build/src/libjthread.a ./target/$1
- mv ./build/src/libjthread.so ./target/$1
- echo "move to ./target/$1 finished"
- else
- echo "move error"$()
- fi
- }*/
这里边有三个函数, 第一个用来创建 target 文件夹, 第二个用来创建 abi 架构的文件夹, 第三个用于复制库到创建好的文件夹下
构建函数
- function build_lib() {
- clean_build
- cd build
- cmake .. \
- -DCMAKE_SYSTEM_NAME=Android \
- -DCMAKE_SYSTEM_VERSION=21 \
- -DCMAKE_ANDROID_ARCH_ABI=$1 \
- -DCMAKE_ANDROID_NDK=$NDK_PATH \
- -DCMAKE_ANDROID_STL_TYPE=gnustl_static
- }
- function create_all_child_dir() {
- for dir in ${TARGETS[@]}; do
- create_child_dir $dir
- echo "$dir created"
- done
- }
- function build_armeabi() {
- prepare_build
- prepare_target
- create_child_dir armeabi
- (
- build_lib armeabi
- make
- )
- move_to_target armeabi
- }
- function build_armeabi-v7a() {
- prepare_build
- prepare_target
- create_child_dir armeabi-v7a
- (
- build_lib armeabi_v7a
- make
- )
- move_to_target armeabi-v7a
- }
- function build_arm64-v8a() {
- prepare_build
- prepare_target
- create_child_dir arm64-8a
- (
- build_lib arm64-v8a
- make
- )
- move_to_target arm64-8a
- }
- function build_mips() {
- prepare_build
- prepare_target
- create_child_dir mips
- (
- build_lib mips
- make
- )
- move_to_target mips
- }
- function build_mips64() {
- prepare_build
- prepare_target
- create_child_dir mips64
- (
- build_lib mips64
- make
- )
- move_to_target mips64
- }
- function build_x86() {
- prepare_build
- prepare_target
- create_child_dir x86
- (
- build_lib x86
- make
- )
- move_to_target x86
- }
- function build_x86_64() {
- prepare_build
- prepare_target
- create_child_dir x86_64
- (
- build_lib x86_64
- make
- )
- move_to_target x86_64
- }
- function create_all_target() {
- prepare_build
- prepare_target
- create_all_child_dir
- for target in ${TARGETS[@]}; do
- (
- build_lib $target
- make
- )
- move_to_target $target
- done
看起来很长的一段函数, 其实没毛的内容, 第一个函数是用来交叉编译的主要函数, 他接受一个平台参数, 也就是我们定义好的 TARGETS 数组中的元素, 后边的七个函数是分别构建单一 abi 架构最后一个就牛逼了, 他会遍历数组, 构建所有的 abi 平台
到这里主要功能完成了
自动补全
为了让程序更完美, 我做了一个自动补全的命令, 在 bash 中安 tab 即可自动补全你想要的架构
- function sbuild() {
- echo "-------$1"
- case $1 in
- "all")
- create_all_target
- ;;
- "armeabi")
- build_armeabi
- ;;
- "v7a")
- build_armeabi-v7a
- ;;
- "v8a")
- build_arm64-v8a
- ;;
- "mips")
- build_mips
- ;;
- "mips64")
- build_mips64
- ;;
- "x86")
- build_x86
- ;;
- "x86_64")
- build_x86_64
- ;;
- "*") ;;
- esac
- }
- sbuild_list=(${TARGETS[@]} "all")
- function _sbuild() {
- local cur
- COMPREPLY=()
- cur="${COMP_WORDS[COMP_CWORD]}"
- COMPREPLY=($(compgen -W "${sbuild_list[*]}" -- ${cur}))
- return 0
- }
- complete -o filenames -F _sbuild sbuild
在这里我们扩展了一下 TARGETS 数组, 添加了一个 all, 这个参数就是用来构建全平台 abi
执行命令
最后执行一下, 最好在 bash 中执行, zsh 好像没有自动补全的 complete 命令
source build.sh
这个命令是用来输出执行脚本, 然后我们就可以用构建命令了
sbuild all
构建全平台
sbuild armeabi
构建 armeabi 平台七种架构都能单独构建
单独构建后只有一种平台架构:
- .
- armeabi
- libjthread.a
- libjthread.so
- 1 directory, 2 files
全部构建后:
- .
- arm64-v8a
- libjthread.a
- libjthread.so
- armeabi
- libjthread.a
- libjthread.so
- armeabi-v7a
- libjthread.a
- libjthread.so
- mips
- libjthread.a
- libjthread.so
- mips64
- libjthread.a
- libjthread.so
- x86
- libjthread.a
- libjthread.so
- x86_64
- libjthread.a
- libjthread.so
- 7 directories, 14 files
最后说明一下, 这个方式是用 cmake 来构建安卓平台的动态库和静态库的方式, 生成好的文件最后还是要放进安卓指定的文件夹中, 这些库的调用必须是用 jni 编程来实现的, 不能直接来调用
来源: https://juejin.im/entry/5a8e8cd8f265da4e6e2bd846