在去年,我曾用 electron 写过一个分析使用 CocoaPods 项目依赖的工具,当时对 CocoaPods 还不怎么了解,用了一种「非正常」的操作解决了我当时的需求。不过现在,我找到了一种更好的解决方案

先回顾一下我们的需求

podspec 中列出的 dependecy 在某些情况下要远远比看起来的多得多,尤其是在大量使用 development pods 的情况下。而有些情况,需要确认一个 pod 的全部依赖(比如为这个 pod 创建 Example 工程的时候),这时就需要一个工具来分析了。最终的目标是在拥有一个 podfile 的情况下,把每个依赖的所有依赖都找出来

上一个方案存在的问题

如果忘记了之前原理的同学可以快速地翻一下这个部分,目前看来,这个方案有几个问题,

  1. Pods/Local Podspecs 里只会存在使用 url 方式引用的 pod。换句话说,如果是以版本号的方式引用的 pod,那它的描述就不会出现在那个目录中。根据之前的分析逻辑,我们就假设它没有依赖了,这样分析完的依赖是不全的
  2. 分析依赖需要 pod install 一次,有些重

那怎样才能优雅而且正确地实现这个需求呢?答案就是使用 CocoaPods 本身 🤓

Analyzer

通过查看 CocoaPods 的源码,我发现了一个类叫做 Analyzer,它有一个 analyze 方法 会返回一个 AnalysisResult 的结构,其中包含一个名字叫 specifications 的 Array。这就应该我们要找的答案

Analyzeranalyzer.rb
# Initialize a new instance

# Analyzes the Podfile, the Lockfile, and the sandbox manifest to generate the information relative to a CocoaPods installation.
class Analyzer
# Parameters:
# sandbox (Sandbox) — @see sandbox
# podfile (Podfile) — @see podfile
# lockfile (Lockfile) (defaults to: nil) — @see lockfile
# plugin_sources (Array<Source>) (defaults to: nil) — @see plugin_sources
def initialize(sandbox, podfile, lockfile = nil, plugin_sources = nil) ⇒ Analyzer
# ...
end

# Performs the analysis.
#
# The Podfile and the Lockfile provide the information necessary to
# compute which specification should be installed. The manifest of the
# sandbox returns which specifications are installed.
#
# @param [Bool] allow_fetches
# whether external sources may be fetched
#
# @return [AnalysisResult]
#
def analyze(allow_fetches = true)
# ...
end

所以现在的任务就变成了,构造一个 Analyzer 然后调用它的 analyze 方法。不过在操作之前,有一些概念需要搞清。

CocoaPods 中的几个基本概念

Pod::Config

用于配置 CocoaPods,比如每次 pod install 是否需要执行 pod repo update;specs master repo 位置之类的。可以通过修改 ~/.cocoapods/config.yaml 文件来实现自定义的配置。

Pod::Installer

最重要的一类,负责 pod install 的整个流程。负责读取 PodfilePodfile.lock 中的依赖内容以及对应的版本信息,把依赖变成 Pods 目录中的一个个实体,然后对 project 文件进行修改,以便我们能直接在工程里用上这些依赖。

Pod::Sandbox

可以理解为与 Podfile 同级的 Pod 目录的抽象

[Pod::Podfile] && [Pod::Podfile.lock]

它们两个都在 cocoaPods-core 仓库中,分别代表 podfile 和 podfile.lock

搞事情

本来在写这篇本文之前已经写了一个小脚本以为能完成工作,但在文章写了一半的时候发现逻辑并不完善,于是文章就搁置了

后来花一些时间好好把逻辑优化了一下,觉得其它人应该也能用得上,就把代码发到了 GitHub 上了 (点这里查看)

主要的代码在 analyze.rb 文件中,核心的逻辑如下,

  • 通过 analyzer 找出所有的 specifications
  • 对于每一个 specification,把它的 name 作为 key,它的依赖(在当前项目中使用的 subsbpecdependecies 的合)作为 value,生成一个 map
  • 再次遍历 specification,根据它的依赖还有上一步生成的 map,递归地寻找它的全部依赖

优势

解决了上一个方案中出现的所有问题

  • 能完整地列出整个 podfile 中的所有依赖(不光 podfile 中列出的,还有依赖的依赖)
  • 很好地处理了 subspec 的问题,会根据 podfile 中依赖的 subspec 输出正确的结果。比如 pod 'Texture', '2.7'pod 'Texture', '2.7', subspecs: %w[PINRemoteImage IGListKit Yoga] 就会产生不同的输出
  • 不需要 pod install,非常轻量

限制

  • 分析的项目中需要包含 Podfile 和对应的 xcodeproj 文件,不能只有 Podfile
  • 用法还比较复杂,需要 clone 工程然后运行脚本指定路径,后续考虑做成一个 pod 插件的形式,参考这个 issue