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

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页