35bc2b7bcfa7b2ba91b26de22647e3b9
Cocoapods 是如何做校验的(缺少支持i386导致pod lib lint失败)

Cocoapods痛点

先看下Cocoapods的一个issue #5854

举个具体案例,可能大家或多或少遇到过这个问题,例如我们做一个公司内部开源库或者github开源库,一般都会让它支持Cocoapods,假设你的库包含一些第三方开源,当你将库开发完之后准备将它发布到Cocoapods,都会先跑一个pod lib lint命令对你的库进行校验,校验成功才会push它到仓库。当跑完的时候你会可能会遇到以下这种错误:

也就是说你链接的ILiveSDK(第三方SDK)某个.o文件不支持i386,因为没有找到对应的符号表信息。一般遇到这种情况有两种解决方法:
第一种对你来说最省时省力,只需要让第三方SDK支持i386,但第三方可能告知你他的SDK不再支持i386了;
第二种则需要你去拆解Cocoapods,让它跳过i386的校验,这也是今天这篇文章的内容所在,首先我会先从源码层面分析Cocoapods是如何校验我们写的代码,接着再教大家如何修改源码让我们的Cocoapods只跳过i386的校验而不影响其他CPU架构的校验,保证侵入性最小;

Cocoapods如何做校验的?

Cocoapods在哪

首先我们先找到Cocoapods的源码:

源码在/Library/Ruby/Gems/2.3.0/gems/cocoapods-1.5.0/lib这个路径下,打开我们看到工程的目录结构:

Cocoapods所有的命令都会由 Command模块派发到将对应的类,例如你想看pod install的源码,那它的路径为:

对应的pod lib lint源码文件在这里:

pod lib lint源码解析

我们先查看lint.rb这个源码文件(只摘录关键方法):

module Pod
  class Command
    class Lib < Command
      class Lint < Lib
        def run
          UI.puts
          podspecs_to_lint.each do |podspec|
            validator                = Validator.new(podspec, @source_urls)
            validator.local          = true
            validator.quick          = @quick
            validator.no_clean       = !@clean
            validator.fail_fast      = @fail_fast
            validator.allow_warnings = @allow_warnings
            validator.no_subspecs    = !@subspecs || @only_subspec
            validator.only_subspec   = @only_subspec
            validator.use_frameworks = @use_frameworks
            validator.ignore_public_only_results = @private
            validator.swift_version = @swift_version
            validator.skip_import_validation = @skip_import_validation
            validator.skip_tests = @skip_tests
            validator.validate

            unless @clean
              UI.puts "Pods workspace available at `#{validator.validation_dir}/App.xcworkspace` for inspection."
              UI.puts
            end
            if validator.validated?
              UI.puts "#{validator.spec.name} passed validation.".green
            else
              spec_name = podspec
              spec_name = validator.spec.name if validator.spec
              message = "#{spec_name} did not pass validation, due to #{validator.failure_reason}."

              if @clean
                message << "\nYou can use the `--no-clean` option to inspect " \
                  'any issue.'
              end
              raise Informative, message
            end
          end
        end

        private

        #----------------------------------------#

        # !@group Private helpers

        # @return [Pathname] The path of the podspec found in the current
        #         working directory.
        #
        # @raise  If no podspec is found.
        # @raise  If multiple podspecs are found.
        #
        def podspecs_to_lint
          if !@podspecs_paths.empty?
            Array(@podspecs_paths)
          else
            podspecs = Pathname.glob(Pathname.pwd + '*.podspec{.json,}')
            if podspecs.count.zero?
              raise Informative, 'Unable to find a podspec in the working ' \
                'directory'
            end
            podspecs
          end
        end
      end
    end
  end
end

这个是对pod lib lint的命令的定义,其实它就是一个Command类,当我们执行这个命令的时候,实际上是调用了它的run方法,我们查看run方法内部调用栈,首先调用podspecs_to_lint方法获取执行命令当前目录所有的podspec文件的路径信息,接着遍历每个podspec文件,对应每一个podspec文件实例化一个的Validator对象,对这个对象进行一些参数配置后,这些参数其实是根据pod lib lint命令后面附带参数透传进来的,当配置完毕之后调用Validatorvalidate方法启动对当前podspec的校验。Validator这个类就是校验的关键类,它定义在/Library/Ruby/Gems/2.3.0/gems/cocoapods-1.5.0/lib/cocoapods/validator.rb,我先看看这个类初始化做了什么:

class Validator
def initialize(spec_or_path, source_urls)
      @linter = Specification::Linter.new(spec_or_path)
      @source_urls = if @linter.spec && @linter.spec.dependencies.empty? && @linter.spec.recursive_subspecs.all? { |s| s.dependencies.empty? }
                       []
                     else
                       source_urls.map { |url| config.sources_manager.source_with_name_or_url(url) }.map(&:url)
                     end
    end
end

首先初始化一个Linter对象,Linter类定义在Cocoapod-Core模块,它用于解析podspec文件(原理是通过定制一套DSL解析podspec文件,将文件解析为Specification对象,关于DSL部分因为不涉及校验核心,所以不展开来讲),接着将外部透传进来的repo源保存到source_urls里。
Validatorvalidate方法就是校验逻辑的主体:

def validate
      ...
      perform_linting
      perform_extensive_analysis(a_spec) if a_spec && !quick
      ...
end

这里只讲解perform_lintingperform_extensive_analysis这两个关键的调用,a_spec指的是Linter对象解析podspec文件序列化得到Specification对象,这个对象包含podspec所有信息。

  def perform_linting
      linter.lint
      @results.concat(linter.results.to_a)
    end

perform_linting内部调用上文讲到的Linter对象的lint方法,该方法主要是为了校验序列化得到Specification对象的合法性(是否符合一个podspec文件的定义)。
```
def perform_extensive_analysis(spec)
...
validate_homepage(spec)
validate_screenshots(spec)
validate_social_media_url(spec)
validate_documentation_url(spec)
validate_source_url(spec)

top Created with Sketch.