用Carthage編譯你喜歡的靜態庫
前言
Carthage
的基本用法已經在上一篇文章詳細介紹了,本文主要針對一個問題的解決方案,那就是如何用Carthage
編譯第三方庫爲靜態庫。和pod package
一樣,封裝了xcodebuild
進行編譯,省去了繁瑣的參數設置,提供更便捷方法使用,Carthage
作爲後起之秀,維護力度和pod package
相比,不是一個量級的,端午花了一天學習了下官方的文檔,可以確定Carthage
將會是第三方庫打包成動態庫或者靜態庫的首選。由於項目都還是Objective-C
爲主,因此我們不會全方位接入,而是使用它的打包功能,得力於他的松耦合,無侵入,可以配合Cocoapods
結合使用,賊好用。
問題
Carthage
默認只能編譯成動態庫。
解決方案
- Carthage 0.29.0以下
staticFramework腳本起了一個臨時文件xcconfig
,寫入如下信息進行靜態庫的編譯參數設置
LD = the/path/to/ld.py
EDBUG_INFOMATION_FORMAT = dwarf
該方法是老版本製作靜態庫的方法, 但是在Xcode 9.4之後,原生已經支持了Swift
源碼的靜態庫,我們只需要修改對應的mach-o type
即可。
- Carthage 0.30.0之後
當前版本以及到最新的版本,都已經支持了OC
和Swift
的靜態庫,即使是已經支持了,但是如果你自己做一個庫,要支持Carthage
,默認的mach-o
類型還是要是動態庫類型,那麼和之前的第三方庫的製作和動態庫基本沒有任何區別。
- 庫工程的project要支持shared schemes,
Product > Schemes > Manage Schemes > Shared
- 使用的時候和動態庫一樣都需要手動拖入
Build phase
中的Linked Binary
,咱們等下用local pod
來做,都不需要手動拖
那麼步驟如下:
- 當我們
check out
對應的庫源碼後,找到對應project下面的target,Build Settings
,Linking section
把Mach-O Type
改爲Static Library
- 執行
carthage build --no-use-binaries --platfor ios
,就可以在./Carthage/Build/$(PLATFORM_NAME)/Static
下面生成對應的靜態庫 - 默認的動態庫我們需要添加腳本
carthage copy-frameworks
,咱們現在用的Static Framework
不需要了,直接拖入即可
Why Static Frameworks
說了那麼多,那麼爲什麼我們這裏要選用第三方庫的靜態庫作爲目標文件呢?
首先如果你注入太多動態庫,App的啓動時間就會變慢,也就是main函數之前動態鏈接的過程變久了。因此我們這裏用Carthage
來把動態庫編譯成靜態庫的方式解決這個問題。如果全部是靜態庫,那麼都會被鏈接到App包裏面,這樣看起來包會增大,還有一個方案就是,把多個靜態庫通過一個動態庫的工程包裹,包成一個動態庫進行鏈接,這樣靜態庫不會被打入包裏面,而且也不會有太多的啓動耗時。這裏需要注意的是,也不是和無腦合併使用動態庫,Apple給我們建議是最多6個動態庫是最合適的傳送門
關於靜態庫合併成動態庫需要注意以下:
直接起一個動態庫的project,然後把我們需要組合的靜態庫都拖入Link Binary with Libraries
中,爲了保證所有的靜態庫都會被合併,需要在Build Settings
的OTHER_LDFLAGS
中加入-all-load
參數,強制鏈接所有靜態庫的全部內容,而不是有時候只鏈接合併其中一部分而已。
腳本自動編譯靜態庫
根據上面的介紹,我們需要手動修改Carthage
中Checkouts
中目標Target下對應的Mach-O Type
類型爲Static Library
,這看上去沒有問題,但是如果庫更新後,我們的Checkouts
源碼又會被重置,這顯然很不優雅,如果哪天忘記修改了,又變成動態庫了,因此就有了如下腳本。
先介紹下腳本核心參數:
- Mach-O Type
Build Setting
中的MACH_O_TYPE
決定了目標文件被編譯成哪種。
executable
可執行文件,也就是我們平時的App架構dynamic library
動態庫static framework
靜態庫 咱們現在要的就是這個 ✅staticlib
bundle
資源文件relocatable object file
這種據我瞭解是 多個動態庫或者靜態庫的合併
-
Debug infomation format
要保證xcodebuild
不會生成對應的dYSM
調試文件,否則的話會編譯失敗。因爲靜態庫本身已經包含了debug symbols
。當靜態庫被集成到executable
的app架構中時,App最終打包生成的可執行文件,也會包含dSYM
文件,該文件包含了本身App的符號以及靜態庫的符號,所以我們需要把DEBUG_INFORMATION_FORMAT
僅僅設置dwarf
即可 -
Framework search paths
Carthage
如果編譯的是靜態庫,會額外生成一個/Static
的文件夾存放對應的靜態庫,比如/Carthage/Build/iOS/Static
。
其實正常情況下都不需要設置,所以可以認爲該選項是可選的,主要是因爲庫之前有依賴關係,比如A依賴B這種,可能會發生編譯失敗的情況,因此主動設置下對應的path,也是問題不大的。我們應該不會遇到這個問題,等下我是直接用Cocoapods
來管理這些編譯好的庫文件,我們還是和之前一樣,熟練的編寫podspec
文件依賴即可,簡直不好太輕鬆。
Demo演示
- 1.創建工程
pod lib create CarthageProj
- 2.創建Cartfile
vim Cartfile
# 寫入如下兩個庫
# github "AFNetworking/AFNetworking" == 3.1.0
# github "Masonry/Masonry" == 0.6.3
- 3.Check out
carthage update --no-use-binaries --platform iOS --no-build
*** Fetching Masonry
*** Checking out AFNetworking at "3.1.0"
*** Checking out Masonry at "v0.6.3"
該腳本和之前的不同,跟了三個參數,分別代表不用遠程編譯的預編譯二進制,只用本地的源碼編譯,第二個參數代表架構,第三個代表不進行編譯,只要把源碼check out
即可,編譯我們等下用自己的腳本進行編譯,別忘了我們上邊是需要用到臨時的xcconfig
文件編譯靜態庫的,這裏如果直接編譯,那就是默認的動態庫
- 4.封裝編譯腳本
./build-static-carthage.sh -p ios -d AFNetworking Masonry
-p | --platform
必須指定ios
,macos
,watchos
,tvos
其中之一,而且只能指定單個
-d | --dependencies
需要編譯的Framework數組,從Cartfile
文件中拿就行了
腳本輸出日誌如下:
Building static frameworks with Xcode temporary xconfig file:
/tmp/static.xcconfig.4GLgSU
With contents:
MACH_O_TYPE = staticlib
DEBUG_INFORMATION_FORMAT = dwarf
FRAMEWORK_SEARCH_PATHS = $(inherited) ./Carthage/Build/iOS/**
Building with command:
carthage build --no-use-binaries --platform ios AFNetworking Masonry
*** xcodebuild output can be found in /var/folders/wh/hhkfnl5d3_j9lsvyjrdcfnv80000gn/T/carthage-xcodebuild.Cpoz1c.log
*** Building scheme "AFNetworking iOS" in AFNetworking.xcworkspace
*** Building scheme "Masonry iOS" in Masonry.xcworkspace
當腳本編譯完後,我們需要的靜態庫就出現在了/Cartahge/Build/iOS/Static
下
- 5.引入工程
A、不用cocoapods
我們需要手動引入,拖入項目中或者Link Binary
那裏,然後最重要的是你要手動生成一個xxx.xcconfig
的文件,然後在project中的Configurations
中比如Debug
和Release
中指定對應的xcconfig
文件名稱,其內容如下
FRAMEWORK_SEARCH_PATH = $(PROJECT_DIR)/ ../Carthage/Build/iOS/Static
OTHER_LINKER_FLAGS = -all_load -ObjC
FRAMEWORK_SEARCH_PATH
指定了庫文件的搜索路徑
OTHER_LINKER_FLAGS
告訴編譯器要把靜態庫完完全全的鏈接到可執行文件中來。-ObjC
代表庫文件中包含了Objective-C extensions
B、使用cocoapods
這裏爲了Demo簡單,我們用local pod
進行演示,效果和遠程pod是一樣的,只是遠程需要lint以下。
新建一個文件夾Library
,把剛纔生成的庫都給導入到這裏,然後對應的庫下面寫一個podspec
文件,該文件從哪來呢?正好,直接從Carthage/Checkouts
中的源碼中有,只是他作用的依賴是源碼,我們把它複製過去,修改成依賴vendored_frameworks
的方式,所有的靜態庫都這麼操作
然後編寫Podfile
文件
# Uncomment the next line to define a global platform for your project
platform :ios, '10.0'
target 'CarthageProj' do
pod 'AFNetworking', :path => 'Library/AFNetworking'
pod 'Masonry', :path => 'Library/Masonry'
target 'CarthageProjTests' do
inherit! :search_paths
# Pods for testing
end
end
執行 pod install
,打開CarthageProj.xcworkspace
可以看到,這幾個xcconfig
文件是cocoapods
幫我們生成的,也就是和我們自己手動添加一樣道理,只是這裏的參數更多更全,原理是一樣的,所以大家熟悉cocoapods
的話,組合起來用,是不是非常爽。
- 最終目的
通過靜態庫的依賴,撇去了源碼的依賴,可以看到我們對AFNetworking
和Masonry
已經沒有任何文件的編譯信息了,這就是二進制化的好處,也是我們後續要做的,優化項目編譯打包時間。
結論
通過這個端午的研究,發現通過Carthage
打包靜態庫是個可行的方案,至少打包第三方庫是可行的。二進制方案的一個環節算是又有了一個新的思路。