本文作者:Edmond
校对:冬瓜
CocoaPods 历险记 这个专题是 Edmond 和 冬瓜 共同撰写,对于 iOS / macOS 工程中版本管理工具 CocoaPods 的实现细节、原理、源码、实践与经验的分享记录,旨在帮助大家能够更加了解这个依赖管理工具,而不仅局限于
pod install
和pod update
。
本文知识目录

引子
在上文 「CocoaPods 命令解析 - CLAide」 中,我们通过对 CLAide 的源码分析,了解了 CocoaPods 是如何处理 pod
命令,多级命令又是如何组织和嵌套的,并解释了命令行输出所代表的含义。今天我们开始学习 Podfile
。
大多 iOS 工程师最先接触到的 CocoaPods 概念应该是 Podfile
,而 Podfile
属于 cocoapods-core
(以下简称 Core) 的两大概念之一。另外一个则是 Podspec[2] (用于描述 Pod Library 的配置文件),只有当你需要开发 Pod 组件的时候才会接触。
在介绍 Podfile 的内容结构之前,必须要谈谈 Xcode 的工程结构。
Xcode 工程结构
我们先来看一个极简 Podfile 声明:
target 'Demo' do
pod 'Alamofire', :path => './Alamofire'
end
它编译后的工程目录如下:

如你所见 Podfile 的配置是围绕 Xcode 的这些工程结构:Workspace、Project、Target 以及 Build Setting 来展开的。作为包管理工具 CocoaPods 将所管理的 Pods 依赖库组装成一个个 Target,统一放入 Pods project
中的 Demo target
,并自动配置好 Target 间的依赖关系。
之后将 Example
主工程和 Pods
工程一起打包到新建的 Example.workspace
,配好主工程与 Pods
工程之间的依赖,完成最终转换。
接下来,我们来聊一聊这些 Xcode 结构:
Target - 最小可编译单元
首先是 Target,它作为工程中最小的可编译单元,根据 Build Phases[3] 和 Build Settings[4] 将源码作为输入,经编译后输出结果产物。
其输出结果可以是链接库、可执行文件或者资源包等,具体细节如下:
Build Setting:比如指定使用的编译器,目标平台、编译参数、头文件搜索路径等;
Build 时的前置依赖、执行的脚本文件;
Build 生成目标的签名、Capabilities 等属性;
Input:哪些源码或者资源文件会被编译打包;
Output:哪些静态库、动态库会被链接;
Project - Targets 的载体
Project 就是一个独立的 Xcode 工程,作为一个或多个 Targets 的资源管理器,本身无法被编译。Project 所管理的资源都来自它所包含的 Targets。特点如下:
至少包含一个或多个可编译的 Target;
为所包含的 Targets 定义了一份默认编译选项,如果 Target 有自己的配置,则会覆盖 Project 的预设值;
能将其他 Project 作为依赖嵌入其中;
下图为 Project 与所包含对 Targets 的关系

Workspace - 容器
作为纯粹的项目容器,Workspace 不参与任何编译链接过程,仅用于管理同层级的 Project,其特点:
Workspace 可以包含多个 Projects;
同一个 Workspace 中的 Proejct 文件对于其他 Project 是默认可见的,这些 Projcts 会共享
workspace build directory
;一个 Xcode Project 可以被包含在多个不同的 Workspace 中,因为每个 Project 都有独立的 Identity,默认是 Project Name;

Scheme - 描述 Build 过程
Scheme 是对于整个 Build 过程的一个抽象,它描述了 Xcode 应该使用哪种 Build Configurations[5] 、执行什么任务、环境参数等来构建我们所需的 Target。
Scheme 中预设了六个主要过程:Build、Run、Test、Profile、Analyze、Archive。包括了我们对 Target 的所有操作,每一个过程都可以单独配置。

CocoaPods-Core
CocoaPods-Core 用于 CocoaPods 中配置文件的解析,包括 Podfile
、Podspec
以及解析后的依赖锁存文件,如 Podfile.lock 等。
CocoaPods-Core 的文件构成
照例,我们先通过入口文件 lib/cocoapods-core.rb
来一窥 Core 项目的主要文件:
module Pod
require 'cocoapods-core/gem_version'
class PlainInformative < StandardError; end
class Informative < PlainInformative; end
require 'pathname'
require 'cocoapods-core/vendor'
# 用于存储 PodSpec 中的版本号
autoload :Version, 'cocoapods-core/version'
# pod 的版本限制
autoload :Requirement, 'cocoapods-core/requirement'
# 配置 Podfile 或 PodSpec 中的 pod 依赖
autoload :Dependency, 'cocoapods-core/dependency'
# 获取 Github 仓库信息
autoload :GitHub, 'cocoapods-core/github'
# 处理 HTTP 请求
autoload :HTTP, 'cocoapods-core/http'
# 记录最终 pod 的依赖信息
autoload :Lockfile, 'cocoapods-core/lockfile'
# 记录 SDK 的名称和 target 版本
autoload :Platform, 'cocoapods-core/platform'
# 对应 Podfile 文件的 class
autoload :Podfile, 'cocoapods-core/podfile'
# 管理 PodSpec 的集合
autoload :Source, 'cocoapods-core/source'
# 管理基于 CDN 来源的 PodSpec 集合
autoload :CDNSource, 'cocoapods-core/cdn_source&#