iOS | 交叉編譯 工欲善其事,必先利其器

前言


在iOS設(shè)備上進行音視頻的處理,往往要使用市面上比較流行的音視頻相關(guān)庫,比如音頻編碼的Lame,處理視頻的FFmpeg,處理圖片的OpenCv等.

而要能夠運行這些庫,我們必須將它們編譯為能夠在iOS設(shè)備上運行的文件.這就需要我們進行交叉編譯.交叉編譯是iOS通向音視頻處理的第一道攔路虎,非常容易出錯.本文最后以編譯Lame庫為例,介紹交叉編譯的完整流程.

1 交叉編譯

1.1 CPU的類型和指令集

對于一臺電腦或者手機來說,核心部件是中央處理器CPU,所有指令的執(zhí)行,最終都是依靠CPU來完成的.

指令集,是CPU能夠執(zhí)行的指令的集合,每種類型CPU都有自己的指令集,或者架構(gòu).

如果是不同系列CPU,指令集是互不兼容的,在指令集為A的CpuA上運行的代碼,不能在指令集為B的CpuB上執(zhí)行.
如果是相同系列的CPU,指令集向下兼容,低版本指令集A1的CpuA1上運行的代碼,也可以在高版本指令集為A2的CpuA2上運行,但是不能夠充分發(fā)揮CpuA2的性能.

1.2 iOS設(shè)備架構(gòu)列表

iOS設(shè)備都雖然都是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上運行,需要經(jīng)過編譯,鏈接,成為PC可執(zhí)行的二進制文件,然后才能在PC上運行.

同理,如果要在其他機器上運行,就必須編譯,鏈接成為可以在其他機器上運行的代碼.

源代碼在機器A上的編譯鏈接得到機器B上運行的代碼.
如果機器B==機器A,那么就是本機編譯.
如果機器B!=機器A,就是交叉編譯.

比如,機器A是Mac,機器B是iPhone,那么就是在Mac上交叉編譯生成iPhone的代碼.

那么為什么不直接在iPhone上本機編譯呢,因為iPhone上沒有成熟的編譯工具,以及足夠的硬件條件去編譯源代碼.

1.4 iOS關(guān)于架構(gòu)的設(shè)置

iOS中,有3個關(guān)于架構(gòu)的設(shè)置:
BuildSettings|Architectures|Architectures
指定工程被編譯成可支持哪些指令集類型,而支持的指令集越多,就會編譯出包含多個指令集代碼的數(shù)據(jù)包,對應(yīng)生成二進制包就越大,也就是ipa包會變大。

BuildSettings|Architectures|Valid Architectures
限制可能被支持的指令集的范圍,也就是Xcode編譯出來的二進制包類型最終從這些類型產(chǎn)生,而編譯出哪種指令集的包,將由Architectures與Valid Architectures(因此這個不能為空)的交集來確定.

BuildSettings|Architectures|Build Active Architecture Only
是否只生成支持當前連接設(shè)備的指令集的包.
一般Debug模式下設(shè)置為YES,加快編譯速度;而Release模式下設(shè)置為NO,更好地適配各種機型.

一般情況下,我們編譯armv7s和arm64的包就可以適配大部分機型了.

2 交叉編譯Lame庫

2.1 Lame庫

Lame是一款效果很好的開源Mp3編碼器,是在iOS設(shè)備上將音頻編碼為Mp3的最佳選擇.
本章用最新版本3.100進行編譯.

2.2 configure命令

這里先提一個文件,configure文件:



每一個符合GNU標準的軟件包都會包含該命令.

交叉編譯的整個流程,做了很多事情,我也不是很清楚具體都做了什么.我們需要關(guān)心的是,我們需要做什么:
我們需要通過以合適的方式運行configure命令,改名令執(zhí)行的結(jié)果會生成合適的Makefile文件.
然后利用make和make install命令編譯和安裝整個庫.

所以,我們在交叉編譯中需要做的重點,就是用合適的參數(shù)去運行configure命令.

2.2 configure命令常用參數(shù)

我們輸入命令./configure -h可以查看configure的幫助文檔,會顯示當前軟件包的全部可選項配置.
下面介紹幾個主要的配置:

  • 指定執(zhí)行編譯任務(wù)的主機的架構(gòu)
    --build={Build}
    build: 執(zhí)行代碼編譯的主機的架構(gòu),正常的話就是你的主機系統(tǒng)。
    這個參數(shù)一般由config.guess來猜就可以。當然自己指定也可以。

  • 指定輸出位置
    --prefix={PREFIX}, 指定編譯好的庫放在哪個目錄下,這是GNU大部分庫的標準配置.

  • 指定要運行庫的架構(gòu)
    --host={HOST},指定要交叉編譯出來的庫,最終運行的平臺,不同的架構(gòu)CPU有不同的值:
    arm64:
    arm-apple-darwin
    armv7:
    arm-apple-darwin
    i386:
    i386-apple-darwin
    x86_64:
    x86_64-apple-darwin

  • 工具鏈的路徑
    CC,交叉編譯工具鏈的路徑,其實就是gcc編譯器的路徑.不同架構(gòu)的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

  • 編譯參數(shù)
    CFLAGS,編譯時需要帶的參數(shù),不同架構(gòu)的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表示該靜態(tài)庫使用了bitcode,關(guān)于bitcode,請參考后面的bitcode一節(jié).

  • 鏈接參數(shù)
    LDFLAGS,鏈接時的參數(shù),和CFLAGS保持一致.

  • 指定關(guān)閉動態(tài)鏈接
    --disable-shared:關(guān)閉動態(tài)鏈接,以便工具可以單獨使用.

  • 指定不編譯可執(zhí)行文件.
    --disable-frontend,指定不編譯LAME的可執(zhí)行文件.

  • 指定創(chuàng)建靜態(tài)庫
    --enable-static,創(chuàng)建靜態(tài)庫,默認為YES.

軟件工程師的職責(zé)之一,就是要把重復(fù)性的東西做成工具,讓工作變得簡單,從而創(chuàng)造快樂和價值.在本文末尾,會給出一個編譯腳本.利用該腳本,可以方便地編譯出i386,x86_64,armv7s,arm64架構(gòu)的庫.

2.3 查看編譯成果物

每一個指令集的包中,有l(wèi)ib,include,share3個目錄:



lib目錄,存放的是我們的靜態(tài)庫文件,libmp3lame.a
include目錄,是工程中需要使用的頭文件,每個指令集對應(yīng)的目錄下的頭文件是一樣的.
share目錄,包含了使用手冊.

2.4 合并靜態(tài)庫

如果我們多個指令集的靜態(tài)庫,需要把他們合并才能使用.
如果這幾個靜態(tài)庫的文件路徑是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會將程序編譯為一個中間表現(xiàn)形式(bitcode)
2,AppStore會將該中間形式的代碼進行優(yōu)化
總體來說,開啟bitcode可以優(yōu)化app的性能;

一個項目要使用bitcode,必須滿足該項目中所有使用的第三方靜態(tài)庫都在編譯時打開了bitcode.否則,只能關(guān)閉bitcode,從而得不到性能上的優(yōu)化.

3 編譯腳本的使用

編譯腳本是一個sh文件,使用時先將腳本放到LAME庫文件的根目錄下.
然后在命令行中,切換到LAME庫文件的目錄下,然后執(zhí)行:
sh ./LameBuilderForiOS.sh
該腳本會編譯i386 x86_64 arm64 armv7s四個平臺的庫文件,輸出在{LAME庫目錄}/thin文件夾中,然后將這四個庫文件合并起來,輸出在{LAME庫目錄}/fat文件夾中.

編譯腳本地址和編譯好的Lame庫地址:
https://github.com/GikkiAres/LameBuilder

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容