cocoapods-meitu-bin

简介

cocoapods-meitu-binCocoaPods 的二进制插件,提供两类核心能力:

  1. 基于壳工程/组件工程制作二进制(pod bin build-all)。
  2. pod install 期间动态切换源码与二进制(含 monorepo :path 组件)。

对应 monorepo :path 动态切换的关键实现可参考提交:6ca9994support worktree identity for local path pods)。

安装

支持两种方式:

  1. 安装到本机 RubyGems。
  2. 通过项目 Gemfile 管理。

方式 1:安装到本机

sudo gem install cocoapods-meitu-bin

方式 2:Gemfile

Gemfile 中添加:

gem 'cocoapods-meitu-bin'

然后执行:

bundle install

快速使用

1) Podfile 开启二进制

plugin 'cocoapods-meitu-bin'

# true: 开启二进制;false: 全源码
use_binaries!(ENV['MEITU_USE_BINARIES'] != 'false')

# 源码白名单(这些组件强制走源码)
set_use_source_pods ['WCDBSwift']

# 可选:按环境切换二进制配置(Debug / Release / Distribution)
set_configuration(ENV['MEITU_USE_CONFIGURATION'] || 'Debug')

2) monorepo 本地 :path 接入示例

MTXX_MONOREPO_ROOT = File.expand_path('../../..', __dir__)
MTXX_MONOREPO_POD_PATHS = {
  'MTVIPModule' => 'Modules/MTVIPModule',
  'MTImageEditor' => 'Modules/MTXXImageModule'
}.freeze

def mtxx_monorepo_pod(name, fallback = nil, fallback_options = nil)
  if MTXX_MONOREPO_POD_PATHS.key?(name)
    options = { :path => File.join(MTXX_MONOREPO_ROOT, MTXX_MONOREPO_POD_PATHS[name]) }
    pod name, options
  elsif fallback_options
    pod name, fallback, fallback_options
  elsif fallback
    pod name, fallback
  else
    pod name
  end
end

target 'MTXX' do
  mtxx_monorepo_pod 'MTVIPModule'
  mtxx_monorepo_pod 'MTImageEditor'
end

Monorepo 专章::path 组件动态切换(重点)

本节专门说明 monorepo 下本地 :path 组件如何在一次 pod install 中动态决定“走源码”还是“走二进制”。

目标与结论

  1. 目标:本地组件有改动就切源码,没改动且二进制可用就切 binary。
  2. 结论:判定依据不是单一 commit,而是 worktree_identity(覆盖 HEAD + staged + unstaged + untracked)。
  3. 命中 binary:local path pod 从 Development Pods 提升为 binary pod。
  4. 未命中 binary:自动回退源码,不阻断依赖分析流程。

核心实现入口

文件 作用
lib/cocoapods-meitu-bin/helpers/worktree_identity.rb 计算 worktree_identity,提供规则版本和 git/fallback 双路径。
lib/cocoapods-meitu-bin/native/resolver.rb resolve / resolver_specs_by_target 阶段执行 source/binary 决策与替换。
lib/cocoapods-meitu-bin/helpers/buildAll/bin_helper.rb 生成 binary version,优先使用 worktree_identity
lib/cocoapods-meitu-bin/config/config.rb (PodUpdateConfig) 缓存 identity、checkout options、依赖关系、promote 状态。
lib/cocoapods-meitu-bin/source_provider_hook.rb source_provider/pre_install 阶段收集 :path/:commit 上下文。

worktree_identity 生成规则(详细)

规则版本:v1_git_tree_diff_untracked_sha1

计算对象:当前一次依赖分析涉及的本地组件目录(通常是 local podspec 所在目录)。

base_tree_sha   = git rev-parse HEAD:<relative_path>
staged_diff_sha = sha1(git diff --cached --binary -- <path>)
unstaged_sha    = sha1(git diff --binary -- <path>)
untracked_sha   = sha1(untracked_file_list + file_contents)

worktree_identity = sha1(base_tree_sha + "|" + staged_diff_sha + "|" + unstaged_sha + "|" + untracked_sha)

字段解释:

  1. base_tree_sha:模块在 HEAD 下的快照标识(提交态)。
  2. staged_diff_sha:已 git add 但未提交的改动。
  3. unstaged_sha:工作区未暂存改动。
  4. untracked_sha:未跟踪文件列表与内容摘要。

为什么必须包含 unstaged/untracked

  1. 只用提交态会误把“本地已改但未提交”组件当成可复用旧 binary。
  2. 新增文件(最常见)如果不纳入 identity,会出现错误命中旧二进制。

回退策略:

  1. 若目录不在 git 仓库内,或 git 查询失败,则回退为“目录相对路径 + 文件内容”摘要。
  2. 回退后仍能保证 identity 随文件变化而变化。

在依赖分析中的实际执行过程(按时序)

  1. :pre_install / :source_provider: 插件加载并收集 Podfile 中 :path:commit 等信息,写入 PodUpdateConfig
  2. Resolver#resolve 前半段: 识别本轮本地 root(sandbox.development_pods + Podfile external_source[:path])。
  3. Resolver#resolve 预计算: 对本地 root 调用 WorktreeIdentity.for_sandbox_pod,把 identity 写入 PodUpdateConfig
  4. Resolver#resolve 依赖关系: 构建 dependency_relationships,给 binary version 计算提供依赖链输入。
  5. Resolver#resolver_specs_by_target 决策: 对每个 rspec 判定 use_binary,并处理 use_source_pods / 黑名单 / selector。
  6. binary version 计算: BinHelper.version 对 local root 优先取 worktree_identity,其次 commit/tag,最后才是版本号。
  7. 命中 binary: 用 binary source 的 spec 替换 rspec,并执行 promote_local_path_pod_to_binary
  8. 未命中 binary: 保留源码 rspec(必要时补 checkout_source),自动回退源码流程。
  9. root + subspec 一致性: 同一 local root 一旦命中 binary,其余 subspec 强制复用同一 binary version,避免 mixed mode。

状态流转(PodUpdateConfig

状态 含义 主要写入点
worktree_identities root -> identity resolve 预计算与按需补算
checkout_options root -> git/commit/tag 解析 checkout options 后写入
dependency_relationships root -> 依赖身份串 resolve 依赖图构建后写入
promoted_local_pods 已提升为 binary 的本地 root promote_local_path_pod_to_binary
external_source_binary 壳工程制作时可参与处理的 external 组件 resolver 记录

Monorepo 动态切换流程图(详细)

flowchart TD
    A["pod install"] --> B["pre_install/source_provider 收集 :path/:commit"]
    B --> C["resolve: 识别本地 root 集合"]
    C --> D["预计算 worktree_identity 并写入 PodUpdateConfig"]
    D --> E["构建 dependency_relationships"]
    E --> F["resolver_specs_by_target 逐 rspec 判定 use_binary"]
    F --> G{"是否强制源码\\n(use_source_pods / selector / force env)?"}
    G -->|是| H["保留源码 rspec"]
    G -->|否| I["BinHelper.version 计算 binary version\\n优先 identity"]
    I --> J{"binary source 命中?"}
    J -->|命中| K["替换为 binary spec\\npromote local path pod"]
    J -->|未命中| L["回退源码\\n保留 external/local source"]
    H --> M["生成最终 Pods 工程"]
    K --> M
    L --> M

环境变量与开关

变量 默认值 作用
MEITU_USE_BINARIES true(未设时) Podfile 层总开关。设为 false 可全源码。
MEITU_USE_CONFIGURATION Debug 指定二进制版本计算和命中所用配置(如 Debug/Release)。
MEITU_FORCE_LOCAL_PATH_SOURCE false 兼容旧逻辑。设为 trueconfiguration_env == 'dev' 时,本地 :path 组件强制进源码白名单。
MEITU_BIN_WORKTREE_DEBUG 0 设为 1 输出 worktree identity 统计和切换调试日志。
MEITU_BIN_BUILD_LOCAL_PATH_PODS true 控制 pod bin build-all 是否制作本地 :path 组件。设为 false 回退旧行为(跳过本地库)。

二进制制作(build-all)

进入 Podfile 所在目录执行:

pod bin build-all --configuration=Debug

常见参数:

选项 含义
--clean 全部制作完成后清理临时目录
--clean-single 每个组件完成后立刻清理临时目录
--repo-update 先更新 Podfile 里声明的 source 仓库
--full-build 强制全量制作(不复用已存在 binary)
--skip-simulator 跳过模拟器架构制作
--configuration=configName 指定构建配置
--shell-project 基于壳工程产物执行打包

build-all 对不同依赖类型的处理

  1. 本地 :path 组件:默认参与制作。
  2. external source(如 :git/:commit/:tag): 在普通 build-all 流程中,命中 sandbox.checkout_sources 即跳过,不参与制作。 在 --shell-project 流程中,只有当组件被标记进 PodUpdateConfig.external_source_binary 集合时才会参与制作;未命中集合仍跳过。 external_source_binary 集合由 resolver 阶段记录,目的是给后续壳工程制作场景提供“可制作的 external 组件”输入。
  3. 无源码组件:跳过。
  4. 已存在同版本 binary:跳过。

流程图

1) pod install:monorepo 本地 path 与二进制动态切换

flowchart TD
    A["pod install"] --> B["source_provider / pre_install"]
    B --> C["resolve 阶段预计算 local path worktree_identity"]
    C --> D["构建 dependency_relationships"]
    D --> E["resolver_specs_by_target 按 rspec 决策 source/binary"]
    E --> F{"是否强制源码\n(MEITU_FORCE_LOCAL_PATH_SOURCE / use_source_pods)?"}
    F -->|是| G["保留源码(Development Pods)"]
    F -->|否| H["计算 binary version\n(identity/commit/tag/依赖关系)"]
    H --> I{"binary spec 是否命中?"}
    I -->|命中| J["promote local path pod -> binary pod\n移出 Development Pods"]
    I -->|未命中| K["回退源码(保留 :path / external source)"]
    G --> L["生成 Pods 工程/Lockfile"]
    J --> L
    K --> L

2) pod bin build-all:二进制制作流程(含本地 :path

flowchart TD
    A["pod bin build-all"] --> B["读取 BinConfig.yaml + CLI 参数"]
    B --> C["Analyzer 分析 pod_targets"]
    C --> D["遍历每个 pod_target"]
    D --> E{"命中黑名单/不在白名单?"}
    E -->|是| D2["跳过"]
    E -->|否| F{"本地 :path 且\nMEITU_BIN_BUILD_LOCAL_PATH_PODS=false?"}
    F -->|是| D2
    F -->|否| G{"external source 或无源码?"}
    G -->|是| D2
    G -->|否| H["计算 binary version"]
    H --> I{"binary 已存在且非 full-build?"}
    I -->|是| D2
    I -->|否| J["编译产物 -> create_binary"]
    J --> K["zip + upload artifact"]
    K --> L["生成并 push binary podspec"]
    L --> M["汇总 Success/Fail 结果"]
    D2 --> M

常用命令

# 开启调试日志看切换细节
MEITU_BIN_WORKTREE_DEBUG=1 pod install

# 强制本地 :path 组件全源码(应急排障,仅在 Podfile 的 configuration_env == 'dev' 时生效)
MEITU_FORCE_LOCAL_PATH_SOURCE=true pod install

# 本地 :path 组件也参与制作(默认就是 true)
MEITU_BIN_BUILD_LOCAL_PATH_PODS=true pod bin build-all --configuration=Debug

# 回退旧行为:build-all 跳过本地 :path
MEITU_BIN_BUILD_LOCAL_PATH_PODS=false pod bin build-all --configuration=Debug

排障建议

  1. 切换不生效:先开 MEITU_BIN_WORKTREE_DEBUG=1,看 resolved/missingforced_source 统计。
  2. 命中旧 binary:检查是否有未跟踪文件变化、set_use_source_pods 白名单、MEITU_FORCE_LOCAL_PATH_SOURCE
  3. binary 存在但安装失败:检查 binary spec 与 artifact 是否同时可访问。
  4. 本地库制作被跳过:检查 MEITU_BIN_BUILD_LOCAL_PATH_PODS 是否被设为 false

参考