iOS SDK開發系列二之Cocoapods項目編譯原理對比和Xcode環境變量介紹

前言

上一篇介紹了基本的一些參數以及簡單介紹模擬了下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
  1. 首先可以看到xcworkspace,一般安裝完之後也會提示用戶用xcworkspace打開,不能在用之前的xcodeproj打開了,xcworkspace裏面有一個contents文件,這裏沒什麼東西,就是寫了管理那幾個project而已,這裏很明顯,管理了原工程SDKDemo1和Pods裏面的Pods.xcodeproj的兩個工程。
  2. Podfile 依賴描述配置文件
  3. 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 中每一個 PodPodfile.lock 中是否存在
如果存在, 則忽略 Podfile 中的配置,使用 Podfile.lock 中的配置(實際上就是什麼都不做)
如果不存在,則使用 Podfile 中的配置,並寫入 Podfile.lock 中

而另一個常用命令 pod update並不是一個日常更新命令。它的原理是忽略 Podfile.lock 文件,完全使用 Podfile 中的配置,並且更新Podfile.lock。一旦決定使用 pod update,就必須所有團隊成員一起更新。因此在使用 update 前請務必瞭解其背後發生的事情和對團隊造成的影響,並且確保有必要這麼做。

  1. Pods目錄
    1. Pods.xcodeproj Pods工程,所有第三方庫由Pods工程構建,每個第三方庫對應裏面的一個Target,並且這個工程還有一個Pods-xxx的一個Target,這個是用來管理的總Target
    2. 比如Masonry會對應每個Target,都會在Pods目錄下有個對應的目錄
    3. Headers 在Headers下有兩個目錄,Private和Public,第3方庫的私有頭文件會在Private目錄下有對應的頭文件,不過是1個軟鏈接,鏈接到第3方庫的頭文件 第3方庫的Pubic頭文件會在Public目錄下有對應的頭文件,也是軟鏈接
    4. Manifest.lock manifest文件 描述對第三方的依賴在這裏插入圖片描述
    5. 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.xcconfigPods-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等第三方靜態庫。所以編譯順序就是

鏈接
有一個
依賴
main target
Pods Project
Podsxxx Target
第三方庫

對應的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的使用

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章