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工程的?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM