cocoapods-meitu-bin
简介
cocoapods-meitu-bin 是 CocoaPods 的二进制插件,提供两类核心能力:
- 基于壳工程/组件工程制作二进制(
pod bin build-all)。 - 在
pod install期间动态切换源码与二进制(含 monorepo:path组件)。
对应 monorepo :path 动态切换的关键实现可参考提交:6ca9994(support worktree identity for local path pods)。
安装
支持两种方式:
- 安装到本机 RubyGems。
- 通过项目
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.('../../..', __dir__)
MTXX_MONOREPO_POD_PATHS = {
'MTVIPModule' => 'Modules/MTVIPModule',
'MTImageEditor' => 'Modules/MTXXImageModule'
}.freeze
def mtxx_monorepo_pod(name, fallback = nil, = nil)
if MTXX_MONOREPO_POD_PATHS.key?(name)
= { :path => File.join(MTXX_MONOREPO_ROOT, MTXX_MONOREPO_POD_PATHS[name]) }
pod name,
elsif
pod name, fallback,
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 中动态决定“走源码”还是“走二进制”。
目标与结论
- 目标:本地组件有改动就切源码,没改动且二进制可用就切 binary。
- 结论:判定依据不是单一 commit,而是
worktree_identity(覆盖HEAD + staged + unstaged + untracked)。 - 命中 binary:local path pod 从
Development Pods提升为 binary pod。 - 未命中 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)
字段解释:
base_tree_sha:模块在HEAD下的快照标识(提交态)。staged_diff_sha:已git add但未提交的改动。unstaged_sha:工作区未暂存改动。untracked_sha:未跟踪文件列表与内容摘要。
为什么必须包含 unstaged/untracked:
- 只用提交态会误把“本地已改但未提交”组件当成可复用旧 binary。
- 新增文件(最常见)如果不纳入 identity,会出现错误命中旧二进制。
回退策略:
- 若目录不在 git 仓库内,或 git 查询失败,则回退为“目录相对路径 + 文件内容”摘要。
- 回退后仍能保证 identity 随文件变化而变化。
在依赖分析中的实际执行过程(按时序)
:pre_install/:source_provider: 插件加载并收集 Podfile 中:path、:commit等信息,写入PodUpdateConfig。Resolver#resolve前半段: 识别本轮本地 root(sandbox.development_pods + Podfile external_source[:path])。Resolver#resolve预计算: 对本地 root 调用WorktreeIdentity.for_sandbox_pod,把 identity 写入PodUpdateConfig。Resolver#resolve依赖关系: 构建dependency_relationships,给 binary version 计算提供依赖链输入。Resolver#resolver_specs_by_target决策: 对每个 rspec 判定use_binary,并处理use_source_pods/ 黑名单 / selector。- binary version 计算:
BinHelper.version对 local root 优先取worktree_identity,其次commit/tag,最后才是版本号。 - 命中 binary:
用 binary source 的 spec 替换 rspec,并执行
promote_local_path_pod_to_binary。 - 未命中 binary:
保留源码 rspec(必要时补
checkout_source),自动回退源码流程。 - 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 |
兼容旧逻辑。设为 true 且 configuration_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 对不同依赖类型的处理
- 本地
:path组件:默认参与制作。 - external source(如
:git/:commit/:tag): 在普通build-all流程中,命中sandbox.checkout_sources即跳过,不参与制作。 在--shell-project流程中,只有当组件被标记进PodUpdateConfig.external_source_binary集合时才会参与制作;未命中集合仍跳过。external_source_binary集合由 resolver 阶段记录,目的是给后续壳工程制作场景提供“可制作的 external 组件”输入。 - 无源码组件:跳过。
- 已存在同版本 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
排障建议
- 切换不生效:先开
MEITU_BIN_WORKTREE_DEBUG=1,看resolved/missing与forced_source统计。 - 命中旧 binary:检查是否有未跟踪文件变化、
set_use_source_pods白名单、MEITU_FORCE_LOCAL_PATH_SOURCE。 - binary 存在但安装失败:检查 binary spec 与 artifact 是否同时可访问。
- 本地库制作被跳过:检查
MEITU_BIN_BUILD_LOCAL_PATH_PODS是否被设为false。