4. Podfile 的解析逻辑

本文做者:Edmond  
校对:冬瓜

CocoaPods 历险记 这个专题是 Edmond 和 冬瓜 共同撰写,对于 iOS / macOS 工程中版本管理工具 CocoaPods 的实现细节、原理、源码、实践与经验的分享记录,旨在帮助你们可以更加了解这个依赖管理工具,而不只局限于 pod install 和 pod updatehtml

本文知识目录

引子

在上文 CocoaPods 命令解析 - CLAide」 中,咱们经过对 CLAide 的源码分析,了解了 CocoaPods 是如何处理 pod 命令,多级命令又是如何组织和嵌套的,并解释了命令行输出所表明的含义。今天咱们开始学习 Podfileios

大多 iOS 工程师最早接触到的 CocoaPods 概念应该是 Podfile,而 Podfile 属于 cocoapods-core(如下简称 Core) 的两大概念之一。另一个则是 Podspec[2] (用于描述 Pod Library 的配置文件),只有当你须要开发 Pod 组件的时候才会接触。git

在介绍 Podfile 的内容结构以前,必需要谈谈 Xcode 的工程结构。github

Xcode 工程结构

咱们先来看一个极简 Podfile 声明:web

target 'Demo' do
 pod 'Alamofire':path => './Alamofire'
end

它编译后的工程目录以下:算法

如你所见 Podfile 的配置是围绕 Xcode 的这些工程结构:Workspace、Project、Target 以及 Build Setting 来展开的。做为包管理工具 CocoaPods 将所管理的 Pods 依赖库组装成一个个 Target,统一放入 Pods project 中的 Demo target,并自动配置好 Target 间的依赖关系。swift

以后将 Example 主工程和 Pods 工程一块儿打包到新建的 Example.workspace,配好主工程与 Pods 工程之间的依赖,完成最终转换。vim

接下来,咱们来聊一聊这些 Xcode 结构:数组

Target - 最小可编译单元

首先是 Target,它做为工程中最小的可编译单元,根据 Build Phases[3]Build Settings[4] 将源码做为输入,经编译后输出结果产物。xcode

其输出结果能够是连接库、可执行文件或者资源包等,具体细节以下:

  • 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 中配置文件的解析,包括 PodfilePodspec 以及解析后的依赖锁存文件,如 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'
  # 管理基于 Trunk 来源的 PodSpec 集合
  autoload :TrunkSource,    'cocoapods-core/trunk_source'
  # 对应 PodSpec 文件的 class
  autoload :Specification,  'cocoapods-core/specification'
  # 将 pod 信息转为 .yml 文件,用于 lockfile 的序列化
  autoload :YAMLHelper,     'cocoapods-core/yaml_helper'
  # 记录 pod 依赖类型,是静态库/动态库
  autoload :BuildType,      'cocoapods-core/build_type'
  
  ...

  Spec = Specification
end

将这些 Model 类按照对应的依赖关系进行划分,层级以下:

Podfile 的主要数据结构

先来了解 Podfile 的主要数据结构

Specification

Specification 即存储 PodSpec 的内容,是用于描述一个 Pod 库的源代码和资源将如何被打包编译成连接库或 framework,后续将会介绍更多的细节。

TargetDefinition

TargetDefinition 是一个多叉树结构,每一个节点记录着 Podfile 中定义的 Pod 的 Source 来源、Build Setting、Pod 子依赖等

该树的根节点指向 Podfile,而 Podfile 中的 root_target_definitions 则记录着全部的 TargetDefinition 的根节点,正常状况下该 list 中只有一个 root 即 Pods.project

为了便于阅读,简化了大量的 DSL 配置相关的方法和属性并对代码顺序作了调整,大体结构以下:

module Pod
  class Podfile
    class TargetDefinition
      # 父节点: TargetDefinition 或者 Podfile
      attr_reader :parent
      # 子节点: TargetDefinition
      attr_reader :children
      # 记录 tareget 的配置信息
      attr_accessor :internal_hash

      def root?
        parent.is_a?(Podfile) || parent.nil?
      end

      def root
        if root?
          self
        else
          parent.root
        end
      end

      def podfile
        root.parent
      end
       
      # ...
    end
  end
end

对应上一节 Xcode 工程结构中的 Podfile 关系以下:

CocoaPods 正是巧妙利用了 Xcode 工程结构的特色,引入  Pods.project 这一中间层,将主工程的 Pods 依赖所有转接到 Pods.project 上,最后再将 Pods.project 做为主项目的依赖。

尽管这么作也受到了一些质疑和诟病(所谓的侵入性太强),但笔者的观点是,正得益于 Pods.project 这一设计隔绝了第三方依赖库对于主项目的频繁更改,也便于后续的管理和更新,体现了软件工程中的 开放-关闭原则

好比,在 Pod 1.7.0 版本中支持的 Multiple Xcodeproj Generation[6] 就是解决随着项目的迭代而日益增大的 Pods project 的问题。

试想当你的项目中存在上百个依赖库,每一个依赖库的变动都会影响到你的主工程,这将是很是可怕的问题。

Podfile

Podfile 是用于描述一个或多个 Xcode Project 中各个 Targets 之间的依赖关系。

这些 Targets 的依赖关系对应的就是 TargetDefinition 树中的各子节点的层级关系。如前面所说,有了 Podfile 这个根节点的指向,仅需对依赖树进行遍历,就能轻松获取完整的依赖关系

有了这层依赖树,对于某个 Pod 库的更新便是对树节点的更新,即可轻松的分析出这次更新涉及的影响。

简化调整后的 Podfile 代码以下:

require 'cocoapods-core/podfile/dsl'
require 'cocoapods-core/podfile/target_definition'

module Pod
  class Podfile
    include Pod::Podfile::DSL
    # podfile 路径
    attr_accessor :defined_in_file
    # 全部的 TargetDefinition 的根节点, 正常只有一个,即 Pods.project target
    attr_accessor :root_target_definitions
    # 记录 Pods.project 项目的配置信息
    attr_accessor :internal_hash
    # 当前 DSL 解析使用的 TargetDefinition
    attr_accessor :current_target_definition

    # ...
  end
end

直接看 dsl.rb,该文件内部定义了 Podfile DSL 支持的全部方法。经过 include 的使用将 Pod::Podfile::DSL 模块 Mix-in 后插入到 Podfile 类中。想了解更多 Mix-in 特性,移步 「CocoaPods 中的 Ruby 特性之 Mix-in」

Lockfile

Lockfile,顾名思义是用于记录最后一次 CocoaPods 所安装的 Pod 依赖库版本的信息快照。也就是生成的 Podfile.lock

pod install 过程,Podfile 会结合它来确认最终所安装的 Pod 版本,固定 Pod 依赖库版本防止其自动更新。

Lockfile 也做为 Pods 状态清单 (mainfest),用于记录安装过程的中哪些 Pod 须要被删除或安装或更新等。

以开头的 Podfile 经 pod install 所生成的 Podfile.lock 为例:

PODS:
  - Alamofire (4.6.0)

DEPENDENCIES:
  - Alamofire (from `./Alamofire`)

EXTERNAL SOURCES:
  Alamofire:
    :path: "./Alamofire"

SPEC CHECKSUMS:
  Alamofire: 0dda98a0ed7eec4bdcd5fe3cdd35fcd2b3022825

PODFILE CHECKSUM: da12cc12a30cfb48ebc5d14e8f51737ab65e8241

COCOAPODS: 1.10.0.beta.2

咱们来分析一下,经过该 Lockfile 可以获取哪些信息:

Key 含义
PODS 记录全部 Pod 库的具体安装版本号
DEPENDENCIES 记录各 Pod 库之间的相互依赖关系,因为这里只有 Alamofire 且它无其余依赖,暂时无关看出区别
EXTERNAL SOURCES 记录部分经过外部源的 Pod 库(Git 引入、Path 引入)
SPEC CHECKSUMS 记录当前各 Pod 库的 Podspec 文件 Hash 值,其实就是文件的 md5
PODFILE CHECKSUM 记录 Podfile 文件的 Hash 值,一样是 md5,确认是否有变动
COCOAPODS 记录上次所使用的 CocoaPods 版本

Podfile 内容加载

Podfile 文件类型

你能够在 CocoaPods 的 /lib/cocoapods/config.rb 找到 Podfile 所支持的文件类型:

PODFILE_NAMES = [
   'CocoaPods.podfile.yaml',
   'CocoaPods.podfile',
   'Podfile',
   'Podfile.rb',
].freeze

CocoaPods 按照上述命名优先级来查找工程目录下所对应的 Podfile 文件。当发现目录中存在 CocoaPods.podfile.yaml 文件时会优先加载。

不少同窗可能只知道到 Podfile 支持 Ruby 的文件格式,而不了解它还支持了 YAML 格式。YAML 是 YAML Ain't Markup Language 的缩写,其官方定义[7]以下:

它是一种面向工程师友好的序列化语言。咱们的 Lockfile 文件就是以 YAML 格式写入 Podfile.lock 中的。

Podfile 文件读取

回到 lib/cocoapods-core/podfile.rb 来看读取方法:

module Pod
  class Podfile
    include Pod::Podfile::DSL

    def self.from_file(path)
      path = Pathname.new(path)
      unless path.exist?
        raise Informative, "No Podfile exists at path `#{path}`."
      end
      # 这里咱们能够看出,Podfile 目前已经支持告终尾是 .podfile 和 .rb 后缀的文件名
      # 实际上是为了改善不少编译器使用文件后缀来确认 filetype,好比 vim
      # 相比与 Podfile 这个文件名要更加的友好
      case path.extname
      when '''.podfile''.rb'
        Podfile.from_ruby(path)
      when '.yaml'
        # 如今也支持了 .yaml 格式
        Podfile.from_yaml(path)
      else
        raise Informative, "Unsupported Podfile format `#{path}`."
      end
    end
end

from_filepod install 命令执行后的 verify_podfile_exists! 中被调用的:

def verify_podfile_exists!
    unless config.podfile
        raise Informative, "No `Podfile' found in the project directory."
    end
end

Podfile 文件的读取就是 config.podfile  里触发的,代码在 CocoaPods 的 config.rb 文件中:

def podfile_path_in_dir(dir)
    PODFILE_NAMES.each do |filename|
        candidate = dir + filename
        if candidate.file?
        return candidate
        end
    end
    nil
end

def podfile_path
    @podfile_path ||= podfile_path_in_dir(installation_root)
end

def podfile
    @podfile ||= Podfile.from_file(podfile_path) if podfile_path
end

这里的方法 podfilepodfile_path 都是 lazy 加载的。最后 Core 的 from_file 将依据目录下的 Podfile 文件类型选择调用 from_yaml 或者 from_ruby

Pod::Command::Install 命令到 Podfile 文件加载的调用栈以下:

Podfile From Ruby 解析

当咱们经过 pod init 来初始化 CocoaPods 项目时,默认生成的 Podfile 名称就是 Podfile,那就从 Podfile.from_ruby 开始。

def self.from_ruby(path, contents = nil)
    # ①
    contents ||= File.open(path, 'r:utf-8', &:read)
    # 兼容 1.9 版本的 Rubinius 中的编码问题
    if contents.respond_to?(:encoding) && contents.encoding.name != 'UTF-8'
        contents.encode!('UTF-8')
    end

    # 对 Podfile 中不规范的单引号或双引号进行检查,并进行自动修正,及抛出错误
    if contents.tr!('“”‘’‛'%(""'''))
        CoreUI.warn "..."
    end
    # ②
    podfile = Podfile.new(path) do
        begin
         eval(contents, nil, path.to_s)
        rescue Exception => e
         message = "Invalid `#{path.basename}` file: #{e.message}"
         raise DSLError.new(message, path, e, contents)
        end
    end
    podfile
end

是对 Podfile 内容的读取和编码,同时对可能出现的单引号和双引号的匹配问题进行了修正。pathblock 为入参进行 podfile 类的初始化并将其放回,保存在全局的 config.podfile 中。

Tips: 若是要在 Ruby 对象的初始化中传入参数,须要重载 Object 的 initialize[8] 方法,这里的 Podfile.new(...) 本质上是 initialize 的方法调用。

initialize 方法所传入的尾随闭包 block 的核心在于内部的 eval 函数(在 CocoaPods 核心组件[9] 中有提到):

eval(contents, nil, path.to_s)

它将 Podfile 中的文本内容转化为方法执行,也就是说里面的参数是一段 Ruby 的代码字符串,经过 eval 方法能够直接执行。继续看 Podfile 的 initialize 方法:

def initialize(defined_in_file = nil, internal_hash = {}, &block)
    self.defined_in_file = defined_in_file
    @internal_hash = internal_hash
    if block
        default_target_def = TargetDefinition.new('Pods'self)
        default_target_def.abstract = true
        @root_target_definitions = [default_target_def]
        @current_target_definition = default_target_def
        instance_eval(&block)
    else
        @root_target_definitions = []
    end
end

它定义了三个参数:

参数 定义
defined_in_file Podfile 文件路径
internal_hash 经过 yaml 序列化获得的 Podfile 配置信息,保存在 internal_hash
block 用于映射 Podfile 的 DSL 配置

须要注意的是,经过 from_ruby 初始化的 Podfile 只传入了参数 1 和 3,参数 2 internal_hash 则是提供给 from_yaml 的。

block 存在,会初始化名为 Pods 的 TargetDefinition 对象,用于保存 Pods project 的相关信息和 Pod 依赖。而后调用 instance_eval[10] 执行传入的 block,将 Podfile 的 DSL 内容转换成对应的方法和参数,最终将参数存入 internal_hash 和对应的 target_definitions 中。

Tips: 在 Ruby 中存在两种不一样的方式来执行代码块 block,分别是 instance_evalclass_evalclass_eval 的执行上下文与调用类相关,调用者是类名或者模块名,而 instance_eval 的调用者能够是类的实例或者类自己。细节看 StackOverFlow[11]

Podfile From YAML 解析

YAML 格式的 Podfile 加载须要借助 YAMLHelper 类来完成,YAMLHelper 则是基于 yaml[12] 的简单封装。

def self.from_yaml(path)
    string = File.open(path, 'r:utf-8', &:read)
  
    # 为了解决 Rubinius incomplete encoding in 1.9 mode
   # https://github.com/rubinius/rubinius/issues/1539
    if string.respond_to?(:encoding) && string.encoding.name != 'UTF-8'
        string.encode!('UTF-8')
    end
    hash = YAMLHelper.load_string(string)
    from_hash(hash, path)
end

def self.from_hash(hash, path = nil)
    internal_hash = hash.dup
    target_definitions = internal_hash.delete('target_definitions'|| []
    podfile = Podfile.new(path, internal_hash)
    target_definitions.each do |definition_hash|
        definition = TargetDefinition.from_hash(definition_hash, podfile)
        podfile.root_target_definitions << definition
    end
    podfile
end

经过 from_yaml 将文件内容转成 Ruby hash 后转入 from_hash 方法。

区别于 from_ruby,这里调用的 initialize 将读取的 hash 直接存入 internal_hash,而后利用 TargetDefinition.from_hash 来完成的 hash 内容到 targets 的转换,所以,这里无需传入 block 进行 DSL 解析和方法转换。

Podfile 内容解析

前面提到 Podfile 的内容最终保存在 internal_hashtarget_definitions 中,本质上都是使用了 hash 来保存数据。因为 YAML 文件格式的 Podfile 加载后就是 hash 对象,无需过多加工。惟一须要处理的是递归调用 TargetDefinition 的 from_hash 方法来解析 target 子节点的数据。

所以,接下来的内容解析主要针对 Ruby 文件格式的 DSL 解析,咱们以 pod 方法为例:

target 'Example' do
 pod 'Alamofire'
end

当解析到 pod 'Alamofire' 时,会先经过 eval(contents, nil, path.to_s 将其转换为 dsl.rb 中的方法:

def pod(name = nil, *requirements)
    unless name
        raise StandardError, 'A dependency requires a name.'
    end
    current_target_definition.store_pod(name, *requirements)
end

name 为 Alamofire,因为咱们没有指定对应的 Alamofire 版本,默认会使用最新版本。requirements  是控制 该 pod 来源获取或者 pod target 的编译选项等,例如:

pod 'Alamofire''0.9'
pod 'Alamofire':modular_headers => true
pod 'Alamofire':configurations => ['Debug''Beta']
pod 'Alamofire':source => 'https://github.com/CocoaPods/Specs.git'
pod 'Alamofire':subspecs => ['Attribute''QuerySet']
pod 'Alamofire':testspecs => ['UnitTests''SomeOtherTests']
pod 'Alamofire':path => '~/Documents/AFNetworking'
pod 'Alamofire':podspec => 'https://example.com/Alamofire.podspec'
pod 'Alamofire':git => 'https://github.com/looseyi/Alamofire.git':tag => '0.7.0'

Tips:requirements 最终是以 Gem::Requirement 对象来保存的。关于 pod 详细说明请移步:Podfile 手册[13]

对 name 进行校验后,直接转入 current_target_definition 毕竟 Pod 库都是存在 Pods.project 之下:

def store_pod(name, *requirements)
  return if parse_subspecs(name, requirements) # This parse method must be called first
  parse_inhibit_warnings(name, requirements)
  parse_modular_headers(name, requirements)
  parse_configuration_whitelist(name, requirements)
  parse_project_name(name, requirements)

  if requirements && !requirements.empty?
    pod = { name => requirements }
  else
    pod = name
  end

  get_hash_value('dependencies', []) << pod
  nil
end

def get_hash_value(key, base_value = nil)
  unless HASH_KEYS.include?(key)
    raise StandardError, "Unsupported hash key `#{key}`"
  end
  internal_hash[key] = base_value if internal_hash[key].nil?
  internal_hash[key]
end

def set_hash_value(key, value)
  unless HASH_KEYS.include?(key)
    raise StandardError, "Unsupported hash key `#{key}`"
  end
  internal_hash[key] = value
end

通过一系列检查以后,调用 get_hash_value 获取 internal_hashdependencies,并将 name 和 requirements 选项存入。

这里的 dependencies key 是定义在 TargetDefinition 文件的 HASH_KEYS,表示 Core 所支持的配置参数:

# freeze 表示该数组不可修改。另外,%w 用于表示其中元素被单引号括起的数组。 
# %W(#{foo} Bar Bar\ with\ space) => ["Foo", "Bar", "Bar with space"] 
# 对应的还有 %W 表示其中元素被双引号括起的数组。
HASH_KEYS = %w(
    name
    platform
    podspecs
    exclusive
    link_with
    link_with_first_target
    inhibit_warnings
    use_modular_headers
    user_project_path
    build_configurations
    project_names
    dependencies
    script_phases
    children
    configuration_pod_whitelist
    uses_frameworks
    swift_version_requirements
    inheritance
    abstract
    swift_version
)
.freeze

整个映射过程以下:

精细化的 Podfile 配置

最后一节让咱们来 Show 一下 ,看看 Podfile 所谓的 targets 之间的依赖关系能够玩出什么花来 😂 。

Target 嵌套

最简单的 Podfile 就是文章开头所展现的,不过在 Podfile 中还能够对 Target 进行嵌套使用。

假设在咱们的主工程同时维护了三个项目,它们都依赖了 Alamofire,经过俄罗斯套娃就能轻松知足条件:

target 'Demo1' do
  pod 'Alamofire'

  target 'Demo2' do
    target 'Demo3' do
    end
  end
end

编译后的 Pods.project 项目结构以下:

咱们知道,CocoaPods 在 Pods.project 中为每一个在 Podfile 中声明的 Target 生成一个与之对应的专属 Target 来集成它的 Pod 依赖。

对于有依赖关系的 Target 其生成的专属 Target 名称则会按照依赖关系叠加来命名,如  target Demo3 的专属 Target 名称为 Pods-Demo1-Demo2-Demo3。安装完成后主项目将会引入该专属 Target 来完成依赖关联,如 Demo3:

关于 Target 嵌套,一个父节点是能够有多个子节点的:

target 'Demo1' do
  pod 'Alamofire'

  target 'Demo2' do
    pod 'RxSwift'
  end
  target 'Demo3' do
   pod 'SwiftyJSON'
  end
end

Abstract Target

上面例子中,因为 Demo1 与 Demo2 都须要依赖 Alamofire,咱们经过 Target 嵌套让 Demo2 来继承 Demo1 的 Pods 库依赖。

这么作可能会有一个限制,就是当 Demo1 的 Pod 依赖并不是所有为 Demo2 所须要的时候,就会有依赖冗余。此时就须要 Abstract Target 登场了。例如:

abstract_target 'Networking' do
  pod 'Alamofire'

  target 'Demo1' do
    pod 'RxSwift'
  end
  target 'Demo2' do
    pod 'ReactCocoa'
  end
  target 'Demo3' do
  end
end

将网络请求的 Pod 依赖抽象到 Networking target 中,这样就能避免 Demo2 对 RxSwift 的依赖。

这种方式配置所生成的 Pods.project 并不会存在名称为 Networking 的 Target,它仅会在主工程的专属 Target 中留下印记:

总结

本文结合 Xcode 工程结构来展开 CocoaPods-Core 的 Podfile 之旅,主要感觉以下:

  1. 再一次感觉了 Ruby 语言的动态之美,给我一个字符串,还你一个未知世界;
  2. 结合 Xcode 工程结构更好的理解了 Podfile 的设计初衷, 基础知识很重要;
  3. 所谓“算法无用论”这种事情,在计算机的世界是不存在的,没有好的数据结构知识如何更好的抽象;
  4. 了解 Podfile 的 DSL 是如何映射到内存中,又是如何来存储每一个关键数据的

知识点问题梳理

这里罗列了四个问题用来考察你是否已经掌握了这篇文章,若是没有建议你加入 收藏 再次阅读:

  1. 说说 TargetDefinition 的数据结构 ?
  2. 说说 TargetDefinition 与 Xcode Project 的关系 ?
  3. Podfile 的文件格式有几种,分别是如何加载 ?
  4. LockfilePodfile 的关系

参考资料

[1]

CocoaPods 命令解析: https://zhuanlan.zhihu.com/p/212101448

[2]

Podspec: https://guides.cocoapods.org/syntax/podspec.html

[3]

Build Phases: https://www.objc.io/issues/6-build-tools/build-process/#controlling-the-build-process

[4]

Build Settings: https://developer.apple.com/library/archive/featuredarticles/XcodeConcepts/Concept-Build_Settings.html#//apple_ref/doc/uid/TP40009328-CH6-SW1

[5]

Build Configurations: https://medium.com/practical-ios-development/some-practical-uses-for-xcode-build-schemes-and-build-configurations-swift-e50d15a1304f

[6]

Multiple Xcodeproj Generation: http://blog.cocoapods.org/CocoaPods-1.7.0-beta/

[7]

官方定义: https://yaml.org/

[8]

initialize: https://ruby-doc.org/docs/ruby-doc-bundle/UsersGuide/rg/objinitialization.html

[9]

CocoaPods 核心组件: https://zhuanlan.zhihu.com/p/187272448

[10]

instance_eval: https://ruby-doc.org/core-2.7.0/BasicObject.html

[11]

StackOverFlow: https://stackoverflow.com/questions/900419/how-to-understand-the-difference-between-class-eval-and-instance-eval

[12]

yaml: https://github.com/ruby/yaml

[13]

Podfile 手册: https://guides.cocoapods.org/syntax/podfile.html#pod





本文分享自微信公众号 - 一瓜技术(tech_gua)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索