pod install安装流程

pod install安装流程

我们先看一下install入口

在命令行中输入which pod,找到pod命令所在的目录

➜  [/Users] ✗ open /usr/local/bin/pod
➜  [/Users] ✗ open /usr/local/bin

根据路径,我们打开pod的脚本,可以看到这个脚本是用来唤起cocoapods的,流程是利用Gem.activate_bin_path 找到 CocoaPods 的安装目录 cocoapods/bin,然后使用 Gem.bin_path 来加载该目录下的 /pod 文件,然后我们打开pod文件,看到Pod::Command.run(ARGV),最终通过pod下面的run方法来进入install。

#!/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/bin/ruby
#
# This file was generated by RubyGems.
#
# The application 'cocoapods' is installed as part of a gem, and
# this file is here to facilitate running it.
#

require 'rubygems'

version = ">= 0.a"

str = ARGV.first
if str
  str = str.b[/\A_(.*)_\z/, 1]
  if str and Gem::Version.correct?(str)
    version = str
    ARGV.shift
  end
end

if Gem.respond_to?(:activate_bin_path)
load Gem.activate_bin_path('cocoapods', 'pod', version)
else
gem "cocoapods", version
load Gem.bin_path("cocoapods", "pod", version)
end

//Pod文件下面的部分代码
require 'cocoapods'

if profile_filename = ENV['COCOAPODS_PROFILE']
  require 'ruby-prof'
  reporter =
    case (profile_extname = File.extname(profile_filename))
    when '.txt'
      RubyProf::FlatPrinterWithLineNumbers
    when '.html'
      RubyProf::GraphHtmlPrinter
    when '.callgrind'
      RubyProf::CallTreePrinter
    else
      raise "Unknown profiler format indicated by extension: #{profile_extname}"
    end
  File.open(profile_filename, 'w') do |io|
    reporter.new(RubyProf.profile { Pod::Command.run(ARGV) }).print(io)
  end
else
  Pod::Command.run(ARGV)
end

下面,我们看看Pod的run函数

module Pod
  class Command
    class Install < Command
    .....
    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
    end
    
  • 判断podfile是否存在,不存在报错"No `Podfile’ found in the project directory."

  • 从podfile和lock文件中获取参数,创建installer

      def installer_for_config
          Installer.new(config.sandbox, config.podfile, config.lockfile)
       end
    
  • 判断spec文件是否应该被更新

  • 不请求更新pod,默认不更新

        # @return [Hash, Boolean, nil] Pods that have been requested to be
        #         updated or true if all Pods should be updated.
        #         If all Pods should been updated the contents of the Lockfile are
        #         not taken into account for deciding what Pods to install.
        #
        attr_accessor :update
    
  • installation是否验证podfile或者lockfile有更新,默认不验证

  • installation是否忽略peoject的缓存内容,默认不忽略

  • 安装pods,开始执行pod isntall的流程

      def install!
          puts 'zyb'
        prepare
        resolve_dependencies
        download_dependencies
        validate_targets
        if installation_options.skip_pods_project_generation?
          show_skip_pods_project_generation_message
        else
          integrate
        end
        write_lockfiles
        perform_post_install_actions
      end
    

    安装过程是线性的,但是需要注意以下两点

    • 为例比main返回旧的podspec文件,在执行解析之前需要清理缓存的podspec文件
    • reslocer可能会触发歪脖的source的下载,需要递归检测外部source 的spec文件

如上所述,在installer组装完毕之后,调用install!方法,进入pod install的流程中,下面我们看看pod install的流程,看看它做了哪些操作

(1)install的准备–prepare

pod install 的环境准备完成,包括版本一致性目录结构以及将 pre-install 的装载插件脚本全部取出,并执行对应的 pre_install hook。

def prepare
  # 如果检测出当前目录是 Pods,直接 raise 终止
  if Dir.pwd.start_with?(sandbox.root.to_path)
    message = 'Command should be run from a directory outside Pods directory.'
    message << "\n\n\tCurrent directory is #{UI.path(Pathname.pwd)}\n"
    raise Informative, message
  end
  UI.message 'Preparing' do
    # 如果 lock 文件的 CocoaPods 主版本和当前版本不同,将以新版本的配置对 xcodeproj 工程文件进行更新
    deintegrate_if_different_major_version
    # 对 sandbox(Pods) 目录建立子目录结构
    sandbox.prepare
    # 检测 PluginManager 是否有 pre-install 的 plugin
    ensure_plugins_are_installed!
    # 执行插件中 pre-install 的所有 hooks 方法
    run_plugins_pre_install_hooks
  end
end

(2)解决依赖冲突–resolve_dependencies

依赖解析过程就是通过 PodfilePodfile.lock 以及沙盒中的 manifest 生成 Analyzer 对象。Analyzer内部会使用 Molinillo (具体的是 Molinillo::DependencyGraph 图算法)解析得到一张依赖关系表。

(1)通过 Analyzer 能获取到很多依赖信息,例如 Podfile 文件的依赖分析结果,也可以从 specs_by_target 来查看各个 target 相关的 specs。

(2)analyze 的过程中有一个 pre_download 的阶段,即在 –verbose 下看到的 Fetching external sources 过程。这个 pre_download 阶段不属于依赖下载过程,而是在当前的依赖分析阶段。

def resolve_dependencies
    # 获取plugin的Sources
    plugin_sources = run_source_provider_hooks
    # 通过 Podfile、Podfile.lock 以及沙盒中的 manifest 生成 Analyzer 对象
    analyzer = create_analyzer(plugin_sources)

    # 如果带有 repo_update 标记
    UI.section 'Updating local specs repositories' do
        # 执行 Analyzer 的更新 Repo 操作
        analyzer.update_repositories
    end if repo_update?

    UI.section 'Analyzing dependencies' do
        # 从 analyzer 取出最新的分析结果,@analysis_result,@aggregate_targets,@pod_targets
        analyze(analyzer)
        # 拼写错误降级识别,确保在白名单内的构建配置没有拼写错误
        validate_build_configurations
    end

    # 如果 deployment 为 true,会验证 podfile & lockfile 是否需要更新
    UI.section 'Verifying no changes' do
        verify_no_podfile_changes!
        verify_no_lockfile_changes!
    end if deployment?
 
    analyzer
end

插曲:pod repo update:用来更新pod资源目录,也就是更新master下的资源,例如,有一个第三方库发布了一个最新的版本,如果不执行pod repo update,那么你的本地是不会知道有一个最新版本的,还会一直以你本地的资源目录为准。不执行这个命令就拿不到这个库的最新版本

我们开发时不使用这个命令时因为pod update是默认会执行一遍pod repo update 。

(3)下载依赖文件–download_dependencies

先执行install_pod_sources 过程,它会调用对应 Pod 的 install! 方法进行资源下载;然后执行 podfile 定义的 pre install 的 hooks,最后根据配置清理 pod sources 信息,主要是清理无用 platform 相关内容

   def download_dependencies
      UI.section 'Downloading dependencies' do
        # 下载、安装文档并清理需要安装的Pod的源代码
        install_pod_sources
        # 运行已安装specs和 Podfile 的pre_install的钩子。
        run_podfile_pre_install_hooks
        #清理pod源
        clean_pod_sources
      end
    end

(4) 验证 targets–validate_targets

验证之前流程中的产物 (pod 所生成的 Targets) 的合法性

  def validate_targets
    #构造TargetValidator
    validator = Xcode::TargetValidator.new(aggregate_targets, pod_targets, installation_options)
    #用来验证提供的聚合和pod是否正确,否则报错
    validator.validate!
  end

  def validate!
    verify_no_duplicate_framework_and_library_names
    verify_no_static_framework_transitive_dependencies
    verify_swift_pods_swift_version
    verify_swift_pods_have_module_dependencies
    verify_no_multiple_project_names if installation_options.generate_multiple_pod_projects?
  end

validate方法,用来验证提供的聚合和pod是否正确,否则报错,下面是对方法内五个函数的介绍

  • verify_no_duplicate_framework_and_library_names

验证是否有重名的 framework,如果有冲突会直接抛出 frameworks with conflicting names 异常。

  • verify_no_static_framework_transitive_dependencies

验证动态库中是否有静态链接库 (.a 或者 .framework) 依赖,如果存在则会触发 transitive dependencies that include static binaries... 错误。假设存在以下场景:

    1. 组件 A 和组件 B 同时依赖了组件 C,C 为静态库,如 Weibo_SDK

    2. 组件 A 依赖组件 B,而组件 B 的 .podspec 文件中存在以下设置时,组件 B 将被判定为存在静态库依赖:

      1. podspec 设置了 s.static_framework = true
      2. podspec 以 s.dependency 'xxx_SDK 依赖了静态库 xxx_SDK
      3. podspec 以 s.vendored_libraries = 'libxxx.a' 方式内嵌了静态库 libxxx

此时如果项目的 Podfile 设置了 use_framework! 以动态链接方式打包的时,则会触发该错误。

问题原因

Podfile 中不使用 use_frameworks! 时,每个 pod 是会生成相应的 .a(静态链接库)文件,然后通过 static libraries 来管理 pod 代码,在 Linked 时会包含该 pod 引用的其他的 pod 的 .a 文件。 Podfile 中使用 use_frameworks! 时是会生成相应的 .framework 文件,然后通过 dynamic frameworks 的方式来管理 pod 代码,在 Linked 时会包含该 pod 引用的其他的 pod 的 .framework 文件。 上述场景中虽然以 framework 的方式引用了 B 组件,然而 B 组件实际上是一个静态库,需要拷贝并链接到该 pod 中,然而 dynamic frameworks 方式并不会这么做,所以就报错了。

解决方案

  1. 修改 pod 库中 podspec,增加 pod_target_xcconfig,定义好 FRAMEWORK_SEARCH_PATHS 和 OTHER_LDFLAGS 两个环境变量;
  2. hook verify_no_static_framework_transitive_dependencies 的方法,将其干掉!对应 issue
  3. 修改 pod 库中 podspec,开启 static_framework 配置 s.static_framework = true
  • verify_swift_pods_swift_version

确保 Swift Pod 的 Swift 版本正确配置且互相兼容的。

  • verify_swift_pods_have_module_dependencies

检测 Swift 库的依赖库是否支持了 module,这里的 module 主要是针对 Objective-C 库而言。 首先,Swift 是天然支持 module 系统来管理代码的,Swift Module 是构建在 LLVM Module 之上的模块系统。Swift 库在解析后会生成对应的 modulemapumbrella.h 文件,这是 LLVM Module 的标配,同样 Objective-C 也是支持 LLVM Module。当我们以 Dynamic Framework 的方式引入 Objective-C 库时,Xcode 支持配置并生成 header,而静态库 .a 需要自己编写对应的 umbrella.h modulemap。 其次,如果你的 Swift Pod 依赖了 Objective-C 库,又希望以静态链接的方式来打包 Swift Pod 时,就需要保证 Objective-C 库启用了 modular_headers,这样 CocoaPods 会为我们生成对应 modulemapumbrella.h 来支持 LLVM Module。你可以从这个地址 - http://blog.cocoapods.org/CocoaPods-1.5.0/ 查看到更多细节。

  • verify_no_pods_used_with_multiple_swift_versions

检测是否所有的 Pod Target 中版本一致性问题。

(5)生成工程-- Integrate

pod install 的最后一步,将之前版本仲裁后的所有组件通过 Project 文件的形式组织起来,并且会对 Project 中做一些用户指定的配置。

def integrate
   #执行已安装的specs和podfile里面的pre_integrate的钩子
    run_podfile_pre_integrate_hooks
   # 进入“Pods/”目录,生成xcode项目
    generate_pods_project
    if installation_options.integrate_targets?
    # 集成用户配置,读取依赖项,使用 xcconfig 来配置
      integrate_user_project
    else
      UI.section 'Skipping User Project Integration'
    end
  end

   作者:土土Edmond木
链接:https://zhuanlan.zhihu.com/p/187272448
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

def generate_pods_project
    # 创建 stage sanbox 用于保存安装前的沙盒状态,以支持增量编译的对比
    stage_sandbox(sandbox, pod_targets)
    # 检查是否支持增量编译,如果支持将返回 cache result
    cache_analysis_result = analyze_project_cache
    # 需要重新生成的 target的列表
    pod_targets_to_generate = cache_analysis_result.pod_targets_to_generate
    # 需要重新生成的 aggregate target
    aggregate_targets_to_generate = cache_analysis_result.aggregate_targets_to_generate

    # 清理需要重新生成 target 的 header 和 pod folders
    clean_sandbox(pod_targets_to_generate)
    # 生成 Pod Project,组装 sandbox 中所有 Pod 的 path、build setting、源文件引用、静态库文件、资源文件等
    create_and_save_projects(pod_targets_to_generate, aggregate_targets_to_generate,
                                cache_analysis_result.build_configurations, cache_analysis_result.project_object_version)

    # SandboxDirCleaner 用于清理增量 pod 安装中的无用 headers、target support files 目录
    SandboxDirCleaner.new(sandbox, pod_targets, aggregate_targets).clean!
    # 更新安装后的 cache 结果到目录 `Pods/.project_cache` 下
    update_project_cache(cache_analysis_result, target_installation_results)
end

(6)写入依赖–write_lockfiles

将依赖更新写入 Podfile.lockManifest.lock

  def write_lockfiles
    @lockfile = generate_lockfile

    UI.message "- Writing Lockfile in #{UI.path config.lockfile_path}" do
      # No need to invoke Sandbox#update_changed_file here since this logic already handles checking if the
      # contents of the file are the same.
      @lockfile.write_to_disk(config.lockfile_path)
    end

    UI.message "- Writing Manifest in #{UI.path sandbox.manifest_path}" do
      # No need to invoke Sandbox#update_changed_file here since this logic already handles checking if the
      # contents of the file are the same.
      @lockfile.write_to_disk(sandbox.manifest_path)
    end
  end

(7)结束回调–perform_post_install_action

install的收尾工作,为所有插件提供 post-installation 操作以及 hook。

  def perform_post_install_actions
    #执行已安装的specs和podfile里面的post install的钩子
    run_plugins_post_install_hooks
    # Prints a warning for any pods that are deprecated
    warn_for_deprecations
    # Prints a warning for any pods that included script phases
    warn_for_installed_script_phases
    # Prints a warning if the project is not explicitly using the git based master specs repo.
    warn_for_removing_git_master_specs_repo
    print_post_install_message
  end

参考:https://www.zhihu.com/people/tu-tu-edmondmu/posts

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在 Kubernetes 环境中,在 Pod安装第三方软件有以下几种常见的方法: 1. 使用容器镜像:最简单的方法是在构建 Pod 的容器镜像时,将所需的第三方软件包包含在镜像中。你可以在 Dockerfile 中使用适当的包管理工具(如 `apt-get`、`yum` 或 `apk`)安装软件包。例如,在 Dockerfile 中使用 `apt-get` 安装软件包的示例: ```dockerfile FROM [BASE_IMAGE] RUN apt-get update && apt-get install -y [PACKAGE_NAME] ``` 2. 使用 init 容器:你可以在 Pod 中定义一个或多个 init 容器,它们可以在主容器启动之前运行,并负责安装所需的第三方软件。这样可以将软件安装过程与应用程序运行过程分离开来。示例: ```yaml apiVersion: v1 kind: Pod metadata: name: my-pod spec: initContainers: - name: install-software image: [INIT_CONTAINER_IMAGE] command: ["sh", "-c"] args: - apt-get update && apt-get install -y [PACKAGE_NAME] containers: - name: main-container image: [MAIN_CONTAINER_IMAGE] # ... ``` 3. 使用 Helm:Helm 是 Kubernetes 的一个包管理工具,它可以帮助你在集群中部署、管理和升级应用程序。你可以使用 Helm Chart 来定义和安装第三方软件。Helm Chart 是一个包含了应用程序部署所需的所有信息的模板。你可以编写自己的 Chart,或使用现有的公共 Chart。示例: ```shell helm install my-app stable/[CHART_NAME] ``` 请根据你的具体需求选择适合的方法来在 Kubernetes Pod安装第三方软件。希望这些信息对你有所帮助!如果还有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员的修养

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值