@qidiandasheng
2022-08-07T12:53:28.000000Z
字数 4963
阅读 1097
Cocoapods
我们下载Cocoapods的源码,然后能看到Gemfile文件,通过这个文件我们能看到cocoapods的gem依赖,这其中就包括了Cocoapods的核心组件依赖。
SKIP_UNRELEASED_VERSIONS = false# Declares a dependency to the git repo of CocoaPods gem. This declaration is# compatible with the local git repos feature of Bundler.#def cp_gem(name, repo_name, branch = 'master', path: false)return gem name if SKIP_UNRELEASED_VERSIONSopts = if path{ :path => "../#{repo_name}" }elseurl = "https://github.com/CocoaPods/#{repo_name}.git"{ :git => url, :branch => branch }endgem name, optsendsource 'https://rubygems.org'gemspecgroup :development docp_gem 'claide', 'CLAide'cp_gem 'cocoapods-core', 'Core', '1-11-stable'cp_gem 'cocoapods-deintegrate', 'cocoapods-deintegrate'cp_gem 'cocoapods-downloader', 'cocoapods-downloader'cp_gem 'cocoapods-plugins', 'cocoapods-plugins'cp_gem 'cocoapods-search', 'cocoapods-search'cp_gem 'cocoapods-trunk', 'cocoapods-trunk'cp_gem 'cocoapods-try', 'cocoapods-try'cp_gem 'molinillo', 'Molinillo'cp_gem 'nanaimo', 'Nanaimo'cp_gem 'xcodeproj', 'Xcodeproj'gem 'cocoapods-dependencies', '~> 1.0.beta.1'gem 'activesupport', '> 5', '< 6' # Pinned < 6 because 6 requires Ruby 2.5.0gem 'bacon', :git => 'https://github.com/leahneukirchen/bacon.git'gem 'mocha', '< 1.5'gem 'mocha-on-bacon'gem 'netrc'gem 'prettybacon'gem 'typhoeus'gem 'webmock'gem 'bigdecimal', '~> 1.3.0'gem 'public_suffix'gem 'ruby-graphviz', '< 1.2.5'# Integration testsgem 'diffy'gem 'clintegracon', :git => 'https://github.com/mrackwitz/CLIntegracon.git'# Code Qualitygem 'inch_by_inch'gem 'rubocop', '0.50.0'gem 'simplecov', '< 0.18'gem 'danger', '~> 5.3'endgroup :debugging dogem 'cocoapods_debug'gem 'rb-fsevent'gem 'kicker'gem 'awesome_print'gem 'ruby-prof', :platforms => [:ruby]end

我们在命令行里输入pod命令,最先来到的就是CLAide,通过调用 Pod::Command.run(ARGV),实例化了一个 CLAide::Command 对象,开始我们的 CLAide 命令解析阶段。
CLAide中最核心的部分就是command.rb里的Command类,我们之后的一些pod命令的起点都是继承自Command。我们在Cocoapods源码下面的command文件夹能看到这些命令,比如Command::Install类对应的命令为pod install:

其中运行部分就是其中的run函数:
module Podclass Commandclass Install < Command# ...def run# 判断是否存在 Podfile 文件verify_podfile_exists!# 从 Config 中获取一个 Instraller 实例installer = installer_for_config# 默认是不执行 updateinstaller.repo_update = repo_update?(:default => false)installer.update = falseinstaller.deployment = @deployment# 忽略工程中的缓存,直接全量编译,默认为falseinstaller.clean_install = @clean_install# install 的真正过程installer.install!endendendend
CocoaPods-Core 用于 CocoaPods 中配置文件的解析,包括 Podfile、Podspec以及解析后的依赖锁存文件,如 Podfile.lock 等。
我们先通过入口文件 lib/cocoapods-core.rb 来一窥 Core 项目的主要文件:
module Podrequire 'cocoapods-core/gem_version'class PlainInformative < StandardError; endclass Informative < PlainInformative; endrequire '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 文件的 classautoload :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 文件的 classautoload :Specification, 'cocoapods-core/specification'# 将 pod 信息转为 .yml 文件,用于 lockfile 的序列化autoload :YAMLHelper, 'cocoapods-core/yaml_helper'# 记录 pod 依赖类型,是静态库/动态库autoload :BuildType, 'cocoapods-core/build_type'...Spec = Specificationend
pod install 命令执行后的 verify_podfile_exists!中读取podfile:
def verify_podfile_exists!unless config.podfileraise Informative, "No `Podfile' found in the project directory."endend
Podfile文件的读取就是config.podfile里触发的,代码在 CocoaPods 的 config.rb 文件中:
def podfile_path_in_dir(dir)PODFILE_NAMES.each do |filename|candidate = dir + filenameif candidate.file?return candidateendendnilenddef podfile_path@podfile_path ||= podfile_path_in_dir(installation_root)enddef podfile@podfile ||= Podfile.from_file(podfile_path) if podfile_pathend
最后 Core 里的podfile.rb的from_file函数将依据目录下的 Podfile 文件类型选择调用 from_yaml 或者 from_ruby:
def self.from_file(path)path = Pathname.new(path)unless path.exist?raise Informative, "No Podfile exists at path `#{path}`."endcase path.extnamewhen '', '.podfile', '.rb'Podfile.from_ruby(path)when '.yaml'Podfile.from_yaml(path)elseraise Informative, "Unsupported Podfile format `#{path}`."endend

读取到文件之后我们需要对Podfile内容进行解析,这里主要看Core下面的podfile.rb文件下的from_ruby函数:
def self.from_ruby(path, contents = nil)contents ||= File.open(path, 'r:utf-8', &:read)...# 生成podfile对象,eval执行自己定义的dsl语句podfile = Podfile.new(path) do# rubocop:disable Lint/RescueExceptionbegin# rubocop:disable Security/Evaleval(contents, nil, path.to_s)# rubocop:enable Security/Evalrescue Exception => emessage = "Invalid `#{path.basename}` file: #{e.message}"raise DSLError.new(message, path, e, contents)end# rubocop:enable Lint/RescueExceptionendpodfileend
Cocoapods-Downloader 是用于下载源码的小工具,它支持各种类型的版本管理工具,包括 HTTP / SVN / Git / Mercurial。它可以提供 tags、commites,revisions,branches 以及 zips 文件的下载和解压缩操作。