NDK(四):交叉编译

上一篇文章中,详细介绍gcc的编译流程,以及静态库和动态库的区别。接下来,就介绍什么是交叉编译,怎样进行交叉编译,也介绍Mac系统上怎样利用iterm2与服务器进行文件传输。

NDK系列文章

[TOC]

什么是交叉编译

介绍交叉编译之前,先介绍一下本地编译。

本地编译

本地编译可以理解为,在当前编译平台下,编译出来的程序只能放到当前平台下运行。比如我上一篇文章中,都是直接在Mac OS平台上编译的,那么就是本地编译,编译出来后也只能再Mac平台上使用,不能放到Android项目中。

交叉编译

交叉编译可以理解为,在当前编译平台下,编译出来的程序能运行在体系结构不同的另一种目标平台上,但是编译平台本身却不能运行该程序。比如接下来我们就要介绍在Mac平台上编写程序,然后编译能够运行在Android平台上的库,就是交叉编译。

为什么需要交叉编译

  • 目标平台的运行速度往往比主机慢得多
  • 整个编译过程是非常消耗资源的,目标平台往往没有足够的内存或磁盘空间

交叉编译Android项目

  1. 下载NDK并解压

    NDK官网下载NDK并进行解压,才能编译出Android平台上的库,否则直接用Mac OS上的gcc只能为本地编译,也可以直接通过Android Studio进行下载。

    image-20190101202815344

  2. 设置环境变量

    定义了路径的变量,接下来编译的时候,就不用输入长长的路径

    1
    ➜  export CC=NDK的路径
    1
    2
    3
    4
    5
    # 我的设置如下,接下来就用指定路径的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
  3. 创建并编写可执行文件

    1
    2
    3
    4
    5
    6
    7
    # main.c文件

    #include <stdio.h>
    int main(){
    printf("hello world\n");
    return 0;
    }
  4. 交叉编译

    1
    2
    3
    4
    5
    ➜  $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中的,所以需要带上文件路径。

  5. 设置文件路径并重新交叉编译

    交叉编译基本的命令与上一篇文章的一样,区别就是把gcc、ar等换成下载的NDK中

    生成动态库

    1
    2
    3
    4
    5
    6
    ➜  $CC --sysroot=$NDK/platforms/android-21/arch-arm -isysroot $NDK/sysroot -isystem $NDK/sysroot/usr/include/arm-linux-androideabi -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

    生成静态库

    1
    2
    3
    4
    5
    6
    7
    8
    # 先生成.o文件(比动态库少了-shared)
    ➜ $CC --sysroot=$NDK/platforms/android-21/arch-arm -isysroot $NDK/sysroot -isystem $NDK/sysroot/usr/include/arm-linux-androideabi -fPIC main.c -o main.o
    # 利用.o文件再生成静态库
    ➜ /Users/guidongyuan/Library/Android/android-ndk-r17c/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-ar r libmain.a main.o
    /Users/guidongyuan/Library/Android/android-ndk-r17c/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-ar: creating libmain.a
    # 查看文件信息
    ➜ file libmain.a
    libmain.a: current ar archive

    参数说明

    --sysroot=<directory>
    设置编译需要的头文件与库文件的查找目录,会分别查找dir/usr/includedir/usr/lib目录下的文件

    查找头文件

    -isysroot directory
    设置头文件的查找目录,会查找directory/usr/include。注意,该查找只用于搜索头文件,而且会覆盖上面--sysroot设置的路径。

    -isystem directory
    -isysroot一样,都是设置头文件的查找路径

    查找库文件

    -Ldirectory
    指定库文件查找目录
    -lxx.so
    指定需要链接的库名

    实例:链接ndk的liblog.so日志库

    1
    2
    3
    # -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的错误,暂时找不到错误原因。

1
2
3
4
5
6
7
8
9
# 拷贝到手机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目录下

1
2
3
4
5
6
7
8
9
# 退出手机的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上呢?解决后顺便在此记录下来

  1. 在Mac上安装Iterm2和lrzsz

    1
    2
    ➜  brew install iterm2
    ➜ brew install lrzsz
  2. 下载github上的脚本,然后copy到/usr/local/bin

    1
    2
    3
    4
    ➜  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
  3. 拷贝iterm2

    具体配置,可以参考上面下载文件夹中的README.MD

    image-20180904082334235

    image-20180904082531465

    1
    2
    3
    4
    5
    6
    7
    8
    9
    Regular 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
  4. 利用ssh连接vps

    1
    ssh 用户名@IP地址 -p 端口号
  5. Linux上安装lrzsz

    1
    root@localhost:~# apt-install lrzsz
  6. 然后就可以利用sz、rz上传下载文件了

    1
    2
    # 如,下载main.c文件到Mac上,执行后选择文件夹保存就可以了
    root@localhost:/home/studyndk# sz main.c

参考资料

公众号:亦袁非猿

欢迎关注,交流学习