前言
上一篇介紹了基本的一些參數以及簡單介紹模擬了下Cocoapods的組合結構,通過workSpace管理了xcodeproject簡單瞭解了多個工程如何調用。這裏我們詳細介紹下Cocoapods和Xcode裏面的環境參數都是些什麼意思。
Cocoapods
它本身就是一個第三方依賴管理工具。支持傳遞依賴,例如A依賴B,B又依賴C,當我們在工程中指定依賴A的時候,它會幫我們自動下載C,並在編譯構建時鏈接C。
這裏主要介紹iOS工程中的一種庫,static library,framework可以順便提一下。當我們新建Target是static library的時候,我們一般使用它編譯出來的libxxx.a文件以及對應的頭文件,在寫應用的時候,把文件拖進項目,後面的一些鏈接路徑搜索一般都是自動的,只要你設置了copy選項。設置Library Search Path搜索靜態庫路徑,設置Header Search Path搜索頭文件。
Static Library
cocoapods管理前
└── SDKDemo1
├── SDKDemo1
└── SDKDemo1.xcodeproj
cocoapods管理後
.
└── SDKDemo1
├── Podfile
├── Podfile.lock
├── Pods
│ ├── Headers
│ ├── Local\ Podspecs
│ ├── Manifest.lock
│ ├── Masonry
│ ├── Pods.xcodeproj
│ └── Target\ Support\ Files
├── SDKDemo1
├── SDKDemo1.xcodeproj
└── SDKDemo1.xcworkspace
└── contents.xcworkspacedata
- 首先可以看到xcworkspace,一般安裝完之後也會提示用戶用xcworkspace打開,不能在用之前的xcodeproj打開了,xcworkspace裏面有一個contents文件,這裏沒什麼東西,就是寫了管理那幾個project而已,這裏很明顯,管理了原工程SDKDemo1和Pods裏面的Pods.xcodeproj的兩個工程。
- Podfile 依賴描述配置文件
- Podfile.lock 當前安裝的依賴庫版本
假設我們在 Podfile 中寫上:pod 'Masonry'
,那麼默認是安裝 Masonry 的最新代碼。這就導致用戶 A 可能裝的是 1.0 版本,而用戶 B 再安裝就變成了 2.0 版本。即使我們在Podfile
中指定了庫的具體版本,那也不能保證不出問題。因爲一個第三方庫還有可能依賴其他的第三方庫,而且不保證它的依賴關係是具體到版本號的
。
因此 Podfile.lock 存在的意義是將某一次 pod install 時使用的各個庫的版本,以及這個庫依賴的其他第三方庫的版本記錄下來,以供別人使用。這樣一來,pod install 的流程其實是:
判斷 Podfile.lock 是否存在,如果不存在,按照 Podfile 中指定的版本安裝
如果 Podfile.lock 存在,檢查 Podfile 中每一個 Pod 在 Podfile.lock 中是否存在
如果存在, 則忽略 Podfile 中的配置,使用 Podfile.lock 中的配置(實際上就是什麼都不做)
如果不存在,則使用 Podfile 中的配置,並寫入 Podfile.lock 中
而另一個常用命令 pod update
並不是一個日常更新命令。它的原理是忽略 Podfile.lock
文件,完全使用 Podfile 中的配置,並且更新Podfile.lock
。一旦決定使用 pod update
,就必須所有團隊成員一起更新。因此在使用 update 前請務必瞭解其背後發生的事情和對團隊造成的影響,並且確保有必要這麼做。
- Pods目錄
Pods.xcodeproj
Pods工程,所有第三方庫由Pods工程構建,每個第三方庫對應裏面的一個Target,並且這個工程還有一個Pods-xxx的一個Target,這個是用來管理的總Target- 比如Masonry會對應每個Target,都會在Pods目錄下有個對應的目錄
- Headers 在Headers下有兩個目錄,Private和Public,第3方庫的私有頭文件會在Private目錄下有對應的頭文件,不過是1個軟鏈接,鏈接到第3方庫的頭文件 第3方庫的Pubic頭文件會在Public目錄下有對應的頭文件,也是軟鏈接
Manifest.lock
manifest文件 描述對第三方的依賴Target Support Files 支撐target的文件
Target\ Support\ Files ├── Masonry │ ├── Masonry-dummy.m │ ├── Masonry-prefix.pch │ └── Masonry.xcconfig └── Pods-SDKDemo1 ├── Pods-SDKDemo1-acknowledgements.markdown ├── Pods-SDKDemo1-acknowledgements.plist ├── Pods-SDKDemo1-dummy.m ├── Pods-SDKDemo1.debug.xcconfig └── Pods-SDKDemo1.release.xcconfig
在Target Support Files目錄下每1個第3方庫都會有1個對應的文件夾,比如Masonry,該目錄下有一個空實現文件dummy
,也有預定義頭文件用來優化頭文件編譯速度pch
,還會有1個xcconfig
文件,該文件會在工程配置中使用,主要存放頭文件搜索目錄,鏈接的Flag(比如鏈接哪些庫)
在Target Support Files
目錄下還會有1個Pods-XXX
的文件夾,該文件夾存放了第3方庫聲明文檔markdown文檔和plist文件,還有1個dummy
的空實現文件,還有debug和release各自對應的xcconfig配置文件,如果是framework另外還有2個腳本文件,Pods-XXX-frameworks.sh腳本用於實現framework庫的鏈接,當依賴的第3方庫是framework形式纔會用到該腳本,另外1個腳本文件: Pods-XXX-resources.sh用於編譯storyboard類的資源文件或者拷貝*.xcassets之類的資源文件
工程結構的變化
當我們用了Cocoapods的時候,會生成xcworkspace用來管理project,這時候我們不能在啓動project,因爲工程文件配置已經發生了變化。
Pods工程
首先我們來看Cocoapods爲我們創建的Project
可以看到Pods Project下面我們只引入了Masonry,但是還有一個Pods-xxx的Target,這個等下再看,這裏的每個Target就是靜態庫文件。根據上面的目錄介紹,每個第三方庫都是靜態庫Target,而且第三方庫都會有一個配置文件xcconfig文件,例如我們的Masonry,會有一個Masonry.xcconfig
,還有個總的Target提供了Pods-SDKDemo1.debug.xcconfig
和Pods-SDKDemo1.release.xcconfig
,會在Project下面根據不同環境配置不同的config文件。
看一下Masonry下面的config文件,由於每個三方庫的config文件沒有被引入工程,我們點開show in finder查看,還是在Target Supporting Files裏面
這個是最新的Cocoapods做的
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Masonry
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/Masonry" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Masonry"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/Masonry
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
來看下老的對比下
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Masonry
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/Masonry" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Masonry"
OTHER_LDFLAGS = -framework "Foundation" -framework "UIKit"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/Masonry
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
可以看到就多了一個OTHER_LDFLAGS
的區別,體現在Build Phrase的Link Binary那裏也能找到,這個東西就是編譯鏈接期間需要鏈接哪些庫,有靜態庫,動態庫或者系統的庫,-l 和-framework
等等,這是Masonry這個Target,對它來說只需要鏈接用到Foundation和UIKit
即可。Header Search Paths就是該Target編譯時查找頭文件的目錄
,新版本的Other Link Flag
不在對應的三方Target下進行鏈接了,而是都寫在了下面的總的Target
再看一下Podsxxx.a對應的Pods-SDKDemo1.debug.xcconfig
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/AFNetworking" "${PODS_ROOT}/Headers/Public/Masonry"
LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry"
OTHER_LDFLAGS = $(inherited) -ObjC -l"Masonry" -framework "CoreGraphics" -framework "Foundation" -framework "MobileCoreServices" -framework "Security" -framework "SystemConfiguration" -framework "UIKit"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
PODS_ROOT = ${SRCROOT}/Pods
這個文件可以看到,總的Pods依賴了Masonry,因此Other_LDFlags除了系統框架,還依賴了Masonry。可以看到總的Target依賴的參數Other_LDFLags依賴了所有的framework以及靜態庫。
主工程變化
根據上面的Pods Target介紹,主工程就不需要單個依賴了,只要在主工程那裏填好Pods-xxx.xcconfig下對應的配置文件即可。然後你在xcconfig配置裏面的參數會對應到Xcode配置系統裏面去,比如你打開Build Setting,然後你搜索下Other Link Flag就能看到配置文件對應OTHER_LDFLAGS
的參數了。
通過介紹,可以看到通過Cocoapods,會在主工程下生成多一個Pods Project,然後統一被xcworkspace管理。能順利編譯是因爲,Pods管理的Target被一個總的Pods-xxx.a所管理,所有的配置都在Pods-xxx.debug/release.xcconfig文件裏面,然後主工程依賴這個xcconfig,Link Binary那裏依賴libPods-xxx.a靜態庫即可,就能順利進行依賴編譯了,下面具體介紹
編譯並鏈接第三方庫的原理
首先根據上面的介紹,主工程會在project那裏生成對應Pods-xxx.config文件的配置,裏面的參數分別如下
1.頭文件的查找
project裏面的configuration已經設置了配置文件,從文件讀取Header Search Path
HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/AFNetworking" "${PODS_ROOT}/Headers/Public/Masonry"
所以主工程可以通過import <xxxx.h>進行查找
2.如何鏈接庫
配置文件搜索Library的參數,而且主工程Link Binary會Link Pods的這個總庫
LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry"
OTHER_LDFLAGS = $(inherited) -ObjC -l"AFNetworking" -l"Masonry" -framework "CoreGraphics" -framework "Foundation" -framework "MobileCoreServices" -framework "Security" -framework "SystemConfiguration" -framework "UIKit"
3.編譯順序
我們的主工程已經在Link Binary那裏顯示指明瞭需要鏈接進來,而這個libPodsxxxx.a正好就是由Pods Project下的Target產生,打開這個Target,可以看到右邊Dependency看到我們所需要的Masonry或者AF等第三方靜態庫。所以編譯順序就是
對應的Framework大致結構不變,多了兩個sh腳本,可以自己試試
Xcode環境變量
上述對應xcconfig文件裏面的環境變量羅列下具體指什麼
1.build成功後的,最終產品路徑--可以在Build Settings參數的Per-configuration Build Products Path項裏設置
$(BUILT_PRODUCTS_DIR)
2.目標工程名稱
$(TARGET_NAME)
3.工程文件(比如xxx.xcodeproj)的路徑
$(SRCROOT)
4.當前工程版本號
$(CURRENT_PROJECT_VERSION)
當編譯靜態庫,未設置任何Build Settings參數時,默認的基礎路徑:
/Users/xxx/Library/Developer/Xcode/DerivedData/SDKDemo-gqxtawcyiuutygbjkgaxrjeqnmpa
下面用$()代替上面一長串東東
$(SYMROOT) = $()/Build/Products
$(BUILD_DIR) = $()/Build/Products
$(BUILD_ROOT) = $()/Build/Products
這三個變量中的$()不會隨着Build Settings參數的設置而改變
相反,以下可以通過設置而改變
$(CONFIGURATION_BUILD_DIR) = $()/Build/Products/Debug-iphonesimulator
$(BUILT_PRODUCTS_DIR) = $()/Build/Products/Debug-iphonesimulator
$(CONFIGURATION_TEMP_DIR) = $()/Build/Intermediates/UtilLib.build/Debug-iphonesimulator
$(TARGET_BUILD_DIR) = $()/Build/Products/Debug-iphonesimulator
$(SDK_NAME) = iphonesimulator12.1
$(PLATFORM_NAME) = iphonesimulator
$(CONFIGURATION) = Debug
$(TARGET_NAME) = libMasonry
$(EXECUTABLE_NAME) = libMasonry.a 可執行文件名
${IPHONEOS_DEPLOYMENT_TARGET} 12.1
$(ACTION) = build
$(CURRENTCONFIG_SIMULATOR_DIR) 當前模擬器路徑
$(CURRENTCONFIG_DEVICE_DIR) 當前設備路徑
$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME =
$()/Build/Products/Debug-iphonesimulator
$(PROJECT_TEMP_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) = $()/Build/Intermediates/UtilLib.build/Debug-iphonesimulator
注意看上面的參數,總有一個你會遇到,Cocoapods最常見的就是他們配置文件裏面的這個
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
那麼這組合起來就是$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME
也就是我們默認編譯後的產物相對路徑
/Users/xxxxxx/Library/Developer/Xcode/DerivedData/SDKDemo-gqxtawcyiuutygbjkgaxrjeqnmpa/Build/Products/Debug-iphonesimulator
這個時候你拿到這些環境變量,再去看xcconfig裏面的參數,就明白具體搜索的路徑了
自定義變量
自定義變量
${CONFIGURATION}-iphoneos 表示:Debug-iphoneos
${CONFIGURATION}-iphonesimulator 表示:Debug-iphonesimulator
$(CURRENTCONFIG_DEVICE_DIR) = ${SYMROOT}/${CONFIGURATION}-iphoneos
$(CURRENTCONFIG_SIMULATOR_DIR) = ${SYMROOT}/${CONFIGURATION}-iphonesimulator
自定義一個設備無關的路徑(用來存放各種架構arm6/arm7/i386/x86_64輸出的產品)
$(CREATING_UNIVERSAL_DIR) = ${SYMROOT}/${CONFIGURATION}-universal
自定義變量代表的值
$(CURRENTCONFIG_DEVICE_DIR) = $()/Build/Products/Debug-iphoneos
$(CURRENTCONFIG_SIMULATOR_DIR) = $()/Build/Products/Debug-iphonesimulator
$(CREATING_UNIVERSAL_DIR) = $()/Build/Products/Debug-universal
靜態庫Build Setting參數介紹
1.Installation Directory:安裝路徑
靜態庫編譯時,在Build Settings中Installation Directory設置“$(BUILT_PRODUCTS_DIR)”
以Cocoapods Masonry爲例,它設置的就是 $(LOCAL_LIBRARY_DIR)/Frameworks
Skip Install設爲YES
Installation Directory默認爲/usr/local/lib
因爲Build Location默認時,.a文件會放在很長(比如:/Users/xxx/Library/Developer/Xcode/DerivedData/xxxProgram
dalrvzehhtesxdfqhxixzafvddwe/Build/Products/Debug-iPhoneos)的路徑下,或是我們target指定的路徑
Skip Install如果是NO,可能會被安裝到默認路徑/usr/local/lib
2.Public Headers Folder Path:對外公開頭文件路徑
設爲“include”(具體的頭文件路徑爲:$(BUILT_PRODUCTS_DIR)/include/xx.h)
cocoapods ==> $(CONTENTS_FOLDER_PATH)/Headers
在最終文件.a同級目錄下生成一個include目錄
默認:/usr/local/include
Public Headers Folder Path這個路徑就是使用這lib的某工程需要依賴的外部頭文件.導入這路徑後,#include/import "xx.h"才能看到
CocoaPods設置的一些參數都是以默認最長的那裏進行設置頭文件和Library Search Path的,比如這個PODS_CONFIGURATION_BUILD_DIR路徑
3.User Header Search Paths:依賴的外部頭文件搜索路徑
設置爲“$(BUILT_PRODUCTS_DIR)/include”
和2中路徑對應
4.Per-configuration Build Products Path:最終文件路徑
比如設爲“../app”,就會在工程文件.xcodeproj上一層目錄下的app目錄裏,創建最終文件
默認爲$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
等於$(BUILT_PRODUCTS_DIR)
5.Per-configuration Intermediate Build Files Path:臨時中間文件路徑
默認爲:$(PROJECT_TEMP_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
6.Code Signing Identity:真機調試的證書選擇
選一個和Bundle identifier相對應的證書
Library Search Paths:庫搜索路徑
Architectures:架構,設爲 armv6 或 armv7
Valid Architectures:應用框架,可以設爲 armv6、 armv7 i386 或 x86_64
Product Name:工程文件名,默認爲$(TARGET_NAME)
Info.plist File:info文件路徑
Build Variants:默認爲normal
Other Linker Flags:其他鏈接標籤
設爲“-ObjC”
當導入的靜態庫使用了類別,需要設爲-ObjC
iOS Deployment Target:ios部署對象
Prefix Header:預編頭文件
Precompile Prefix Header:設爲“Yes”,表示允許加入預編譯頭
第三篇Demo演示
參考文章:
Xcode 環境變量介紹
Cocoapods介紹
Cocoapods官方參數配置
xcconfig文件非官方文檔介紹
xcconfig的使用