Cocoapods之 Podfile文件

在之前的文章中,我们讲述了cocoapods命令解析的方式和方法Cocoapods 命令解析器 CLAide,今天主要来探讨下Podfile文件是怎么解析的。

在探讨之前,我们先了解下,Ruby中的eval的用法

eval

截屏2021-09-14 下午11.10.48.png
eval和之前文章讲的instance_evalRuby和Cocoapods文章合集
有相似的地方,可以以string的形式执行Ruby代码。

p eval("2 + 3") #1

eval ("def sayHello; p 'hello world'; end") #2
eval("sayHello") # 3

输出结果:

5
"hello world"
复制代码
  • 1, 执行 2 + 3 ,所以最后输出 5。
  • 2,定义一个sayHello方法,该方法输出hello world
  • 3,执行sayHello方法。

Podfile文件格式

支持的格式

打开之前我们创建的cocoapods调试工程,并设置断点,查看函数调用栈

截屏2021-09-14 下午11.37.53.png

我们可以快速的定位到self.from_file这个类方法中:

def self.from_file(path)
    path = Pathname.new(path)
    unless path.exist?
        raise Informative, "No Podfile exists at path `#{path}`."
    end
    
    case path.extname
    
    when '', '.podfile', '.rb'
        Podfile.from_ruby(path)
    when '.yaml'
        Podfile.from_yaml(path)
    else
        raise Informative, "Unsupported Podfile format `#{path}`."
    end
end
复制代码

Podfile如果文件的扩展名为'' .podfile .rb中的其中一个,就将该文件识别为Ruby文件,通过 Podfile.from_ruby读取该文件。

如果文件的扩展名为yaml,就通过 Podfile.from_yaml 来读取,在接下来探讨中,我们以Podfile.from_ruby展开来讲。

def self.from_ruby(path, contents = nil)
    contents ||= File.open(path, 'r:utf-8', &:read)
    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
复制代码

我们来查看下contents的值

contents

"platform :ios, '13.0'\n\ntarget 'HQPDemo' do\n use_frameworks!\n pod 'Alamofire'\n\n target 'HQPDemoTests' do\n inherit! :search_paths\n end\n\n target 'HQPDemoUITests' do\n end\n\nend\n\npost_install do |installer|\n installer.pods_project.targets.each do |target|\n puts \"post --\#{target.name}\"\n end\nend\n\npre_install do |installer|\n\n installer.pods_project.target.each do |target|\n puts \"pre --\#{target.name}\"\n end\n\nend \n\n\n"
复制代码

contents是一个字符串,内容是我们在Podfile文件里写的内容,通过eval方法来执行,

platform, target,use_frameworks!方法在哪里进行定义的呢?

DSL

cocoapods-core中的Podfile.rb中:

截屏2021-09-14 下午11.56.04.png
Pod::Podfile::DSLmodule ,使用 inclue引用进来,

截屏2021-09-14 下午11.58.24.png
Pod::Podfile::DSL定义了 Podfile中需要执行的方法。采用Mix-in的方式,让我们的Podfile类,也有了这些方法,从而能够执行上述方法。DSL模块来负责解析我们在Podfile文件里的相关配置。

为方便理解,我写了一个简易版的文件解析

// Podfile
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '13.0'

target 'HQPDemo' do
    use_frameworks! false
    puts "开始安装第三方文件"
end

// real_eval.rb
class ReadEval
    def platform(name, target)
        p "平台:--- #{name}, target:----#{target}"
    end
    def use_frameworks!(option=true)
        p "option: -- #{option}"
    end
    def source(source)
        p "source: -- #{source}"
    end
    def target(name, options = nil)
        p "target--name: -- #{name}"
        p "target--option: -- #{options}"
        yield if block_given?
    end
    def initialize(path ,&block)
        if block
            instance_eval(&block)
        end
    end
    def self.from_ruby(path, contents = nil)
        contents = File.open(path,'r:utf-8',&:read)
        if contents.respond_to?(:encoding) && contents.encoding.name != 'UTF-8'
            contents.encode!('UTF-8')
        end
        
        ReadEval.new(path) do
            eval(contents)
        end
    end
end

path = Dir.pwd + "/Pod/Podfile"
ReadEval.from_ruby(path)

//执行结果:
"source: -- [https://github.com/CocoaPods/Specs.git]" 
"平台:--- ios, target:----13.0" 
"target--name: -- HQPDemo" 
"target--option: -- " 
"option: -- false" 开始安装第三方文件
复制代码

这样就完成了Podfile文件的读取,并执行。

Podfile对象

接下来,我们来看下Podfile对象的结构

class Podfile
    inclue Pod::Podfile::DSL
    
    attr_accessor :defined_in_file # 1
    
    attr_accessor :root_target_definitions #2
     
end
复制代码
  • 1,podfile文件的路径。
  • 2,根Target 定义,是一个数组,在本例中,只有一个元素TargetDefinition(Pods),用来记录 Podsproject。

Targetdefinition

Targetdefinition是一个多叉树结构。通过 @parent@children来记录层级嵌套关系

def initialize(name, parent, internal_hash = nil)
    @internal_hash = internal_hash || {}
    @parent = parent
    @children = []
    @label = nil
    self.name ||= name
    if parent.is_a?(TargetDefinition)
        parent.children << self
    end
end
复制代码

主要作用是根据Podfile文件记录cocoapods依赖的库关系,在Podfile文件中

platform :ios, '13.0'

target 'HQPDemo' do
    use_frameworks!
    pod 'Alamofire'
    
    target 'HQPDemoTests' do
        inherit! :search_paths
    end

    target 'HQPDemoUITests' do

    end
end
复制代码

相对应的Podfile对象的TargetDefinition结构为

截屏2021-09-17 上午10.17.04.png

截屏2021-09-17 上午10.19.11.png

到这里 Podfile文件就读取完成了,存放在配置Config中。下一步开始初始化Pod::Installer对象,开始下载依赖的处理,这一块,在后面的文章里,我们在探讨

参考:
Podfile 的解析逻辑

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享