NDK(四):交叉编译

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

NDK系列文章

  • NDK(一):编写第一个JNI项目
  • NDK(二):JNI与Java回调以及静动态注册
  • NDK(三):静态库和动态库

什么是交叉编译

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

本地编译

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

交叉编译

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

为什么需要交叉编译

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

交叉编译Android项目

  1. 下载NDK并解压

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

    NDK(四):交叉编译_第1张图片
    image-20190101202815344
  2. 设置环境变量

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

    ➜  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
    
  3. 创建并编写可执行文件

    # mian.c文件
    
    #include 
    int main(){
        printf("hello world\n");
        return 0;
    }
    
  4. 交叉编译

    ➜  $CC -fPIC main.c -o main
    main.c:1:19: fatal error: stdio.h: No such file or directory
     #include 
                       ^
    compilation terminated.
    

    上面编译运行错误,提示找不到.h文件。因为gcc是用ndk中的,所以.h也需要用ndk中的。所以需要带上文件路径

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

    # 生成静态库
    ➜  $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=
    设置编译需要的头文件与库文件的查找目录,会分别查找dir/usr/includedir/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上呢?解决后顺便在此记录下来

  1. 在Mac上安装Iterm2和lrzsz

    ➜  brew install iterm2
    ➜  brew install lrzsz
    
  2. 下载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
    
  3. 拷贝iterm2

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

    NDK(四):交叉编译_第2张图片
    image-20180904082334235
    image-20180904082531465
    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

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

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

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

参考资料

  • 交叉编译详解 一 概念篇
  • GCC编译选项和环境变量
  • adb “Permission denied” to run a “./configure” file
  • Mac使用 iterm2 上传,下载文件

你可能感兴趣的:(NDK(四):交叉编译)