iOS | 交叉编译 工欲善其事,必先利其器

前言


在iOS设备上进行音视频的处理,往往要使用市面上比较流行的音视频相关库,比如音频编码的Lame,处理视频的FFmpeg,处理图片的OpenCv等.

而要能够运行这些库,我们必须将它们编译为能够在iOS设备上运行的文件.这就需要我们进行交叉编译.交叉编译是iOS通向音视频处理的第一道拦路虎,非常容易出错.本文最后以编译Lame库为例,介绍交叉编译的完整流程.

1 交叉编译

1.1 CPU的类型和指令集

对于一台电脑或者手机来说,核心部件是中央处理器CPU,所有指令的执行,最终都是依靠CPU来完成的.

指令集,是CPU能够执行的指令的集合,每种类型CPU都有自己的指令集,或者架构.

如果是不同系列CPU,指令集是互不兼容的,在指令集为A的CpuA上运行的代码,不能在指令集为B的CpuB上执行.
如果是相同系列的CPU,指令集向下兼容,低版本指令集A1的CpuA1上运行的代码,也可以在高版本指令集为A2的CpuA2上运行,但是不能够充分发挥CpuA2的性能.

1.2 iOS设备架构列表

iOS设备都虽然都是ARM系列的CPU,但是有不同的指令集:
armv6:iPhone,iPhone2,iPhone 3G
armv7:iPhone 4,iPhone 4S
armv7s:iPhone 5,iPhone 5S
arm64:iPhone6(P),iPhone7(P)
arm64e:iPhone XS
i386:32位模拟器
x64:64位模拟器

1.3 什么是交叉编译

编译型语言的源代码,比如C源代码,要能在PC上运行,需要经过编译,链接,成为PC可执行的二进制文件,然后才能在PC上运行.

同理,如果要在其他机器上运行,就必须编译,链接成为可以在其他机器上运行的代码.

源代码在机器A上的编译链接得到机器B上运行的代码.
如果机器B==机器A,那么就是本机编译.
如果机器B!=机器A,就是交叉编译.

比如,机器A是Mac,机器B是iPhone,那么就是在Mac上交叉编译生成iPhone的代码.

那么为什么不直接在iPhone上本机编译呢,因为iPhone上没有成熟的编译工具,以及足够的硬件条件去编译源代码.

1.4 iOS关于架构的设置

iOS中,有3个关于架构的设置:
BuildSettings|Architectures|Architectures
指定工程被编译成可支持哪些指令集类型,而支持的指令集越多,就会编译出包含多个指令集代码的数据包,对应生成二进制包就越大,也就是ipa包会变大。

BuildSettings|Architectures|Valid Architectures
限制可能被支持的指令集的范围,也就是Xcode编译出来的二进制包类型最终从这些类型产生,而编译出哪种指令集的包,将由Architectures与Valid Architectures(因此这个不能为空)的交集来确定.

BuildSettings|Architectures|Build Active Architecture Only
是否只生成支持当前连接设备的指令集的包.
一般Debug模式下设置为YES,加快编译速度;而Release模式下设置为NO,更好地适配各种机型.

一般情况下,我们编译armv7s和arm64的包就可以适配大部分机型了.

2 交叉编译Lame库

2.1 Lame库

Lame是一款效果很好的开源Mp3编码器,是在iOS设备上将音频编码为Mp3的最佳选择.
本章用最新版本3.100进行编译.

2.2 configure命令

这里先提一个文件,configure文件:



每一个符合GNU标准的软件包都会包含该命令.

交叉编译的整个流程,做了很多事情,我也不是很清楚具体都做了什么.我们需要关心的是,我们需要做什么:
我们需要通过以合适的方式运行configure命令,改名令执行的结果会生成合适的Makefile文件.
然后利用make和make install命令编译和安装整个库.

所以,我们在交叉编译中需要做的重点,就是用合适的参数去运行configure命令.

2.2 configure命令常用参数

我们输入命令./configure -h可以查看configure的帮助文档,会显示当前软件包的全部可选项配置.
下面介绍几个主要的配置:

  • 指定执行编译任务的主机的架构
    --build={Build}
    build: 执行代码编译的主机的架构,正常的话就是你的主机系统。
    这个参数一般由config.guess来猜就可以。当然自己指定也可以。

  • 指定输出位置
    --prefix={PREFIX}, 指定编译好的库放在哪个目录下,这是GNU大部分库的标准配置.

  • 指定要运行库的架构
    --host={HOST},指定要交叉编译出来的库,最终运行的平台,不同的架构CPU有不同的值:
    arm64:
    arm-apple-darwin
    armv7:
    arm-apple-darwin
    i386:
    i386-apple-darwin
    x86_64:
    x86_64-apple-darwin

  • 工具链的路径
    CC,交叉编译工具链的路径,其实就是gcc编译器的路径.不同架构的CPU有不同的值:
    x86_64:
    xcrun -sdk iphonesimulator clang -arch x86_64
    i386:
    xcrun -sdk iphonesimulator clang -arch i386
    arm64:
    xcrun -sdk iphoneos clang -arch arm64
    armv7s:
    xcrun -sdk iphoneos clang -arch armv7s

  • 编译参数
    CFLAGS,编译时需要带的参数,不同架构的CPU有不同的值:
    x86_64:
    -arch x86_64 -fembed-bitcode -miphoneos-version-min=7.0
    i386:
    -arch i386 -fembed-bitcode -miphoneos-version-min=7.0
    arm64:
    -arch arm64 -fembed-bitcode -miphoneos-version-min=7.0
    armv7s:
    -arch armv7s -fembed-bitcode -miphoneos-version-min=7.0
    其中,-fembed-bitcode表示该静态库使用了bitcode,关于bitcode,请参考后面的bitcode一节.

  • 链接参数
    LDFLAGS,链接时的参数,和CFLAGS保持一致.

  • 指定关闭动态链接
    --disable-shared:关闭动态链接,以便工具可以单独使用.

  • 指定不编译可执行文件.
    --disable-frontend,指定不编译LAME的可执行文件.

  • 指定创建静态库
    --enable-static,创建静态库,默认为YES.

软件工程师的职责之一,就是要把重复性的东西做成工具,让工作变得简单,从而创造快乐和价值.在本文末尾,会给出一个编译脚本.利用该脚本,可以方便地编译出i386,x86_64,armv7s,arm64架构的库.

2.3 查看编译成果物

每一个指令集的包中,有lib,include,share3个目录:



lib目录,存放的是我们的静态库文件,libmp3lame.a
include目录,是工程中需要使用的头文件,每个指令集对应的目录下的头文件是一样的.
share目录,包含了使用手册.

2.4 合并静态库

如果我们多个指令集的静态库,需要把他们合并才能使用.
如果这几个静态库的文件路径是libPath1,libPath2
使用以下命令合并:

$ lipo -create libPath1 libPath2 -output outputFilePath

合并后的库文件的目录为outputFilePath

验证是否合并成功,可使用file命令或者lipo -info命令:

$ file libmp3lame.a 
libmp3lame.a: Mach-O universal binary with 4 architectures: [i386:current ar archive random library] [arm64]
libmp3lame.a (for architecture i386):   current ar archive random library
libmp3lame.a (for architecture armv7s): current ar archive random library
libmp3lame.a (for architecture x86_64): current ar archive random library
libmp3lame.a (for architecture arm64):  current ar archive random library

或者:
$ lipo -info libmp3lame.a 
Architectures in the fat file: libmp3lame.a are: i386 armv7s x86_64 arm64 

2.5 bitcode

如果开启了bitcode
1,Xcode会将程序编译为一个中间表现形式(bitcode)
2,AppStore会将该中间形式的代码进行优化
总体来说,开启bitcode可以优化app的性能;

一个项目要使用bitcode,必须满足该项目中所有使用的第三方静态库都在编译时打开了bitcode.否则,只能关闭bitcode,从而得不到性能上的优化.

3 编译脚本的使用

编译脚本是一个sh文件,使用时先将脚本放到LAME库文件的根目录下.
然后在命令行中,切换到LAME库文件的目录下,然后执行:
sh ./LameBuilderForiOS.sh
该脚本会编译i386 x86_64 arm64 armv7s四个平台的库文件,输出在{LAME库目录}/thin文件夹中,然后将这四个库文件合并起来,输出在{LAME库目录}/fat文件夹中.

编译脚本地址和编译好的Lame库地址:
https://github.com/GikkiAres/LameBuilder

你可能感兴趣的:(iOS | 交叉编译 工欲善其事,必先利其器)