• CocoaPods源码剖析(一)


    更新记录

    • 2020年3月28日,初稿

    源码地址

    • CocoaPods/CocoaPods
    • Pod是由Ruby实现的,所以想要读懂源码,还需要先了解一下Ruby的源码

    源码运行过程(含注释),即输入Pod install(或update)的执行过程

    前置环节-生成对应的Command子类对象(例如Install类和Update类)

    1. Pod::Command的run类方法
    def self.run(argv)
       help! 'You cannot run CocoaPods as root.' if Process.uid == 0 && !Gem.win_platform?
    
       verify_minimum_git_version!
       verify_xcode_license_approved!
    
       super(argv)
       ensure
       UI.print_warnings
    end
    
    1. CLAide::Command类的run方法
    def self.run(argv = [])
       plugin_prefixes.each do |plugin_prefix|
          PluginManager.load_plugins(plugin_prefix)
       end
    
       argv = ARGV.coerce(argv)
       #通过参数生成一个Command类的子类对象
       command = parse(argv)
       ANSI.disabled = !command.ansi_output?
       unless command.handle_root_options(argv)
          command.validate!
          # 调用comman类子类对象的run方法
          command.run
       end
       rescue Object => exception
       handle_exception(command, exception)
    end
    
    1. CLAide::Command类的parse方法
    # @param  [Array, ARGV] argv
    #         A list of (remaining) parameters.
    #
    # @return [Command] An instance of the command class that was matched by
    #         going through the arguments in the parameters and drilling down
    #         command classes.
    #
    def self.parse(argv)self.run
       argv = ARGV.coerce(argv)
       # 得到第一个参数
       cmd = argv.arguments.first
       if cmd && subcommand = find_subcommand(cmd)
          argv.shift_argument
          subcommand.parse(argv)
       elsif abstract_command? && default_subcommand
          load_default_subcommand(argv)
       else
          new(argv)
       end
    end
    

    3.1 CLAide::Command类的argument方法

    # @return [Array<Argument>]
    #         A list of arguments the command handles. This is shown
    #         in the usage section of the command’s help banner.
    #         Each Argument in the array represents an argument by its name
    #         (or list of alternatives) and whether it's required or optional
    #
    def arguments
       # 如果@arguments不为空,则返回@arguments,否则返回空数组
       @arguments ||= []
    end
    

    3.2 CLAide::Command类的find_subcommand方法

    # Searches the list of subcommands that should not be ignored for command
    # lookup for a subcommand with the given `name`.
    #
    # @param  [String] name
    #         The name of the subcommand to be found.
    #
    # @return [CLAide::Command, nil] The subcommand, if found.
    #
    def self.find_subcommand(name)
       subcommands_for_command_lookup.find { |sc| sc.command == name }
    end
    

    3.3 通过 find_subcommand 找到对应的子类对象,然后调用子类对象的parse方法(subcommand.parse(argv))

    def self.parse(argv)
        entries = []
        #对argv数组的每个值进行to_s的表达式操作,生成一个新的数组,存储到copy变量中
        copy = argv.map(&:to_s)
        double_dash = false
        #shift返回数组的第一个元素,并且移除该元素。类比stack的pop函数
        while argument = copy.shift
           # if为真,直接进入下次循环
           next if !double_dash && double_dash = (argument == '--')
           type = double_dash ? :arg : argument_type(argument)
           parsed_argument = parse_argument(type, argument)
           entries << [type, parsed_argument]
        end
        entries
    end
    

    实际pod主流程的核心环节(调用Installer类的install函数)

    • 看到 CocoaPods/lib/cocoapods/command/install.rb中Install类的run方法
    def run
        verify_podfile_exists!
        installer = installer_for_config
        installer.repo_update = repo_update?(:default => false)
        installer.update = false
        installer.deployment = @deployment
        installer.clean_install = @clean_install
        installer.install!
    end
    
    • 我们对比一下 CocoaPods/lib/cocoapods/command/update.rb中Update类的run方法
    def run
        verify_podfile_exists!
    
        installer = installer_for_config
        installer.repo_update = repo_update?(:default => true)
        installer.clean_install = @clean_install
        if @pods.any? || @excluded_pods.any? || @source_pods.any?
           verify_lockfile_exists!
           verify_pods_are_installed!
           verify_excluded_pods_are_installed!
    
           @pods += @source_pods.select { |pod| config.lockfile.pod_names.include?(pod) }
           @pods = config.lockfile.pod_names.dup if @pods.empty?
           @pods -= @excluded_pods
    
           installer.update = { :pods => @pods }
        else
           UI.puts 'Update all pods'.yellow
           installer.update = true
        end
        installer.install!
    end
    
    • 对比Install类和Update类的run方法,我们发现
      • 相同点
        • 最后都会调用Installer类的install!函数
      • 明显的不同点
        • Install命令类的repo_update属性为false,Update命令类的repo_update函数为true
        • Install命令类的update属性为false,Update命令类的update函数为true
    • 参考Podfile.lock背后的那点事,我们可以提前知道update变量的值,区分了Installer调用install函数下,使用pod installpod update的场景。

    本篇结论

    1. 本篇分析执行Pod install或者update的前几个步骤,主要是通过命令行参数(install或者update)的解析,实例化不同的Command子类对象。
    2. install和update参数的命令执行,最后都会进入Installer类的install函数,执行核心的依赖库安装过程。
    3. Installer类的install函数,是通过update的值,来判断两种不同的场景(即update和install)来进行不同的操作
    4. 另附上简单版(真的是简单版,别见怪)类图,流程图

    写在后面的话

    • 鉴于篇幅已较长,为了有较好的阅读体验(避免文章分析过长,很多读者其实都没有耐心看下去),而且自己也不能一下子完全捋清楚,所以第一篇到此为止。
    • 后续会从该篇的节点继续往下分析
    • 后续还需要分析的点
      • Installer类中的update属性如何使用?如何具体地区别install和update两种场景?
      • PodFile.lock文件有何作用?
      • Pod如何解析PodFile文件的?
      • Pod.spec文件有何作用?
      • Pod是如何集成Xcode工程的?
  • 相关阅读:
    HDU5597/BestCoder Round #66 (div.2) GTW likes function 打表欧拉函数
    HDU5596/BestCoder Round #66 (div.2) 二分BIT/贪心
    HDU 5596/BestCoder Round #66 (div.2) GTW likes math 签到
    BZOJ 1877: [SDOI2009]晨跑 费用流
    BZOJ 1452: [JSOI2009]Count 二维树状数组
    BZOJ 1143 1143: [CTSC2008]祭祀river 最长反链
    Codeforces Round #335 (Div. 2) D. Lazy Student 贪心
    Codeforces Round #335 (Div. 2) C. Sorting Railway Cars 连续LIS
    Codeforces Round #335 (Div. 2) A. Magic Spheres 模拟
    UVALive 6187 Never Wait for Weights 带权并查集
  • 原文地址:https://www.cnblogs.com/HelloGreen/p/12589770.html
Copyright © 2020-2023  润新知