NDK(四):交叉编译
上一篇文章中,详细介绍gcc的编译流程,以及静态库和动态库的区别。接下来,就介绍什么是交叉编译,怎样进行交叉编译,也介绍Mac系统上怎样利用iterm2与服务器进行文件传输。
NDK系列文章
什么是交叉编译
介绍交叉编译之前,先介绍一下本地编译。
本地编译
本地编译可以理解为,在当前编译平台下,编译出来的程序只能放到当前平台下运行。比如我上一篇文章中,都是直接在Mac OS平台上编译的,那么就是本地编译,编译出来后也只能再Mac平台上使用,不能放到Android项目中。
交叉编译
交叉编译可以理解为,在当前编译平台下,编译出来的程序能运行在体系结构不同的另一种目标平台上,但是编译平台本身却不能运行该程序。比如接下来我们就要介绍在Mac平台上编写程序,然后编译能够运行在Android平台上的库,就是交叉编译。
为什么需要交叉编译
- 目标平台的运行速度往往比主机慢得多
- 整个编译过程是非常消耗资源的,目标平台往往没有足够的内存或磁盘空间
交叉编译Android项目
-
下载NDK并解压
在NDK官网下载NDK并进行解压,才能编译出Android平台上的库,否则直接用Mac OS上的gcc只能为本地编译,也可以直接通过Android Studio进行下载。
image-20190101202815344 -
设置环境变量
定义了路径的变量,接下来编译的时候,就不用输入长长的路径
➜ export CC=NDK的路径
# 我的设置如下,接下来就用指定路径的gcc去编辑 ➜ export NDK=/Users/guidongyuan/Library/Android/android-ndk-r17c ➜ export CC=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc # 输出NDKPATH变量的内容,可以验证是否设置成功 ➜ echo $CC
-
创建并编写可执行文件
# mian.c文件 #include <stdio.h> int main(){ printf("hello world\n"); return 0; }
-
交叉编译
➜ $CC -fPIC main.c -o main main.c:1:19: fatal error: stdio.h: No such file or directory #include <stdio.h> ^ compilation terminated.
上面编译运行错误,提示找不到.h文件。因为gcc是用ndk中的,所以
.h
也需要用ndk中的。所以需要带上文件路径 -
设置文件路径并重新交叉编译
# 生成静态库 ➜ $CC --sysroot=$NDK/platforms/android-21/arch-arm -isysroot $NDK/sysroot -isystem $NDK/sysroot/usr/include/arm-linux-androideabi -pie -fPIC main.c -o libmain.so ➜ ls libmain.so main.c # 查看文件信息,为executable ➜ file libmain.so libmain.so: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /system/bin/linker, not stripped # 生成动态库(添加-shared参数) ➜ $CC --sysroot=$NDK/platforms/android-21/arch-arm -isysroot $NDK/sysroot -isystem $NDK/sysroot/usr/include/arm-linux-androideabi -pie -fPIC -shared main.c -o libmain.so ➜ ls libmain.so main.c # 查看文件信息,为shared object ➜ file libmain.so ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /system/bin/linker, not stripped
参数说明
--sysroot=<directory>
设置编译需要的头文件与库文件的查找目录,会分别查找dir/usr/include
和dir/usr/lib
目录下的文件查找头文件
-isysroot directory
设置头文件的查找目录,会查找directory/usr/include
。注意,该查找只用于搜索头文件,而且会覆盖上面--sysroot
设置的路径。-isystem directory
与-isysroot
一样,都是设置头文件的查找路径查找库文件
-Ldirectory
指定库文件查找目录
-lxx.so
指定需要链接的库名实例:链接ndk的liblog.so日志库
# -L需要指定到bin目录 gcc -L/Users/guidongyuan/Library/Android/android-ndk-r17c/platforms/android-21/arch-arm/usr/bin -llog gcc --sysroot=/Users/guidongyuan/Library/Android/android-ndk-r17c/platforms/android-21/arch-arm -llog
在Android平台上测试验证
注意:发送到Android平台上,为上面交叉编译出来的so文件,而且为静态库,尝试发送动态库执行,最后提示Illegal instruction
的错误,暂时找不到错误原因。
# 拷贝到手机sdcard中
➜ adb push libmain.so /sdcard/
# 进入手机路径执行
➜ adb shell
➜ cd /sdcard/
# 执行可执行文件
➜ ./libmain.so
# 如果提示该错误,则需要更改到其他路径
can't execute: Permission denied
执行的时候,如果提示上面的错误,可以参考adb “Permission denied” to run a “./configure” file,链接说到,如果确定该文件是可以执行的文件,拷贝到/data/local/tmp
目录下
# 退出手机的shell
➜ exit
# 重新拷贝到手机sdcard中
➜ adb push libmain.so /data/local/tmp
➜ adb shell
➜ cd /data/local/tmp
➜ ./libmain.so
hello world
# 成功输出hello world
Mac使用iterm2上传、下载文件
使用Mac交叉编译的时候,也尝试用我的VPS的Linux系统进行编译测试,但怎样把编译好的文件发送回我的Mac上呢?解决后顺便在此记录下来
-
在Mac上安装Iterm2和lrzsz
➜ brew install iterm2 ➜ brew install lrzsz
-
下载github上的脚本,然后copy到
/usr/local/bin
中➜ cd /tmp ➜ git clone https://github.com/mmastrac/iterm2-zmodem.git ➜ mv /tmp/iterm2-zmodem/iterm2-recv-zmodem.sh /usr/local/bin/iterm2-recv-zmodem.sh ➜ mv /tmp/iterm2-zmodem/iterm2-send-zmodem.sh /usr/local/bin/iterm2-send-zmodem.sh
-
拷贝iterm2
具体配置,可以参考上面下载文件夹中的README.MD
image-20180904082334235 image-20180904082531465Regular expression: rz waiting to receive.\*\*B0100 Action: Run Silent Coprocess Parameters: /usr/local/bin/iterm2-send-zmodem.sh Instant: checked Regular expression: \*\*B00000000000000 Action: Run Silent Coprocess Parameters: /usr/local/bin/iterm2-recv-zmodem.sh Instant: checked
-
利用ssh连接vps
ssh 用户名@IP地址 -p 端口号
-
Linux上安装lrzsz
root@localhost:~# apt-install lrzsz
-
然后就可以利用sz、rz上传下载文件了
# 如,下载main.c文件到Mac上,执行后选择文件夹保存就可以了 root@localhost:/home/studyndk# sz main.c