以jrtplib的依赖库jthread的编译为例,介绍cmake的交叉编译,因为这个库相对简单,构建完成后就可以直接使用,将动态库或者静态库放置到安卓指定的文件夹下,通过引用头文件来编写jni即可。
这篇文章用到了一点cmake交叉编译的东西,在我的掘金主页有关于如何使用cmake的教程。
https://juejin.im/post/5a8ebe006fb9a0635a6574de
文章废话少,干货多,请静下心来看:
关于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替换为自己的路径即可。1
2
3
4
5
6
7 !/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文件夹是构建的文件夹,所有的生成文件都会在这边,所以先检测是否有这个文件夹,没有就创建,有就清除里边的内容。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 清除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对应的文件夹下
1 | function prepare_target() { |
这里边有三个函数,第一个用来创建target文件夹,第二个用来创建abi架构的文件夹,第三个用于复制库到创建好的文件夹下。
构建函数
1 | function build_lib() { |
看起来很长的一段函数,其实没毛的内容,第一个函数是用来交叉编译的主要函数,他接受一个平台参数,也就是我们定义好的TARGETS数组中的元素,后边的七个函数是分别构建单一abi架构。最后一个就牛逼了,他会遍历数组,构建所有的abi平台。
到这里主要功能完成了。
自动补全
为了让程序更完美,我做了一个自动补全的命令,在bash中安tab即可自动补全你想要的架构。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41function 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命令。1
source build.sh
这个命令是用来输出执行脚本,然后我们就可以用构建命令了1
sbuild all
构建全平台1
sbuild armeabi
构建armeabi平台。七种架构都能单独构建。
单独构建后只有一种平台架构:1
2
3
4
5
6.
└── armeabi
├── libjthread.a
└── libjthread.so
1 directory, 2 files
全部构建后:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24.
├── 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编程来实现的,不能直接来调用。