cocoapods-local-override 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Podfile.local.example +22 -0
- data/README.md +105 -0
- data/lib/cocoapods_plugin.rb +659 -0
- metadata +58 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 721bbcd4a8edde7fbe840ecc9b0ea60992f2c14fd6e82d851d739ed2abf08141
|
|
4
|
+
data.tar.gz: 742f596b656dac95e23a447529ef988d67e529f9b2ed4ff1ec045c6867f182fa
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: '09c8e22c52e06e23b9199acade0957f06d21fcc55219178633cd0823f613294f0d971909e0bad38efaa0b4a49f1fb532f8be4dacec2fa3d914d620d5b9e85541'
|
|
7
|
+
data.tar.gz: 7aa64b14d53d6df0edd7af6c1e1566eeea327de8cb72fe84a5d4795aecbde53bc99d090c184bd9853d95fd4a3691a74c0b61c1b0814954a1eee290870b836c69
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Podfile.local — 本地 pod 切换配置
|
|
2
|
+
# 将该文件放在 Podfile 同级目录,由 cocoapods-local-override 插件自动读取。
|
|
3
|
+
# 不需要在 Podfile 中添加任何 require/load。
|
|
4
|
+
|
|
5
|
+
# -----------------------------------------------------------------
|
|
6
|
+
# 1. 本地路径替换(将远程 pod 指向本地目录)
|
|
7
|
+
# -----------------------------------------------------------------
|
|
8
|
+
# local_pods:
|
|
9
|
+
# SomePod: ../path/to/SomePod
|
|
10
|
+
# AnotherPod: ../path/to/AnotherPod
|
|
11
|
+
|
|
12
|
+
# -----------------------------------------------------------------
|
|
13
|
+
# 2. 版本号替换
|
|
14
|
+
# -----------------------------------------------------------------
|
|
15
|
+
# replace_versions:
|
|
16
|
+
# SomePod: 1.2.3
|
|
17
|
+
|
|
18
|
+
# -----------------------------------------------------------------
|
|
19
|
+
# 3. Git 分支替换(仅对通过 :git + :branch 声明的 pod 生效)
|
|
20
|
+
# -----------------------------------------------------------------
|
|
21
|
+
# replace_branches:
|
|
22
|
+
# SomeGitPod: develop
|
data/README.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# cocoapods-local-override
|
|
2
|
+
|
|
3
|
+
在不修改 Podfile 的前提下,将指定 pod 从远程依赖切换为本地路径(或替换版本号/branch)。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 安装
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
cd Business/Tools/cocoapods-local-override
|
|
11
|
+
gem build cocoapods-local-override.gemspec
|
|
12
|
+
gem install --local cocoapods-local-override-*.gem
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
验证安装:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pod plugins installed | grep local-override
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 使用
|
|
24
|
+
|
|
25
|
+
### 1. 创建配置文件
|
|
26
|
+
|
|
27
|
+
插件会在首次 `pod install` 时自动从模板 `Podfile.local.example` 创建 `Podfile.local`,无需手动操作。
|
|
28
|
+
|
|
29
|
+
如需手动创建:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
cp Tools/cocoapods-local-override/Podfile.local.example Podfile.local
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 2. 编辑 Podfile.local
|
|
36
|
+
|
|
37
|
+
```yaml
|
|
38
|
+
local_pods:
|
|
39
|
+
BBreachOrder: ../BusinessLayer/BBreachOrder
|
|
40
|
+
BMediator: ../CoreLayer/BMediator
|
|
41
|
+
BaseConfiguration: ../CoreLayer/BaseConfiguration
|
|
42
|
+
BaseFoundation: ../../BasicLayer/BaseFoundation
|
|
43
|
+
|
|
44
|
+
replace_versions:
|
|
45
|
+
SDWebImage: 1.2.3
|
|
46
|
+
|
|
47
|
+
replace_branches:
|
|
48
|
+
DeliverGoods: develop-9.6.2
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 3. 执行 pod install
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pod install
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
插件自动生效,输出示例:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
[ LOCAL ] BTransportationReminder → ../BusinessLayer/BTransportationReminder
|
|
61
|
+
[ LOCAL ] BBreachOrder → ../BusinessLayer/BBreachOrder
|
|
62
|
+
[ LOCAL ] BMediator → ../CoreLayer/BMediator
|
|
63
|
+
[ VERSION ] SDWebImage → 1.2.3
|
|
64
|
+
[ BRANCH ] DeliverGoods → develop-9.6.2
|
|
65
|
+
[ LOCAL ] WNDatePicker (via BTransportationReminder) → ../../BasicLayer/WNDatePicker/
|
|
66
|
+
[+] Added Podfile.local to Pods project
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 4. 恢复远程依赖
|
|
70
|
+
|
|
71
|
+
注释掉或删除 `Podfile.local` 中对应的条目,或不创建该文件即可恢复远程依赖。
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 功能说明
|
|
76
|
+
|
|
77
|
+
### 三种覆盖模式
|
|
78
|
+
|
|
79
|
+
| 配置项 | 作用 | 标签 |
|
|
80
|
+
|---|---|---|
|
|
81
|
+
| `local_pods` | 将 pod 替换为 `:path` 指向本地目录 | `[ LOCAL ]` |
|
|
82
|
+
| `replace_versions` | 覆盖 Podfile 中声明的版本号 | `[ VERSION ]` |
|
|
83
|
+
| `replace_branches` | 修改 `:git` 声明的 `:branch` | `[ BRANCH ]` |
|
|
84
|
+
|
|
85
|
+
三种配置按上述优先级依次匹配,命中的第一个生效。路径不存在时黄色警告并回退远程。
|
|
86
|
+
|
|
87
|
+
### 传递依赖自动注入
|
|
88
|
+
|
|
89
|
+
本地 pod 的 podspec 中声明的依赖,如果也在 `local_pods` 配置中但未在 Podfile 显式声明,插件会自动将其注入为目标依赖,确保整个依赖树使用本地版本。
|
|
90
|
+
|
|
91
|
+
输出格式:`[ LOCAL ] dep_name (via parent) → path`
|
|
92
|
+
|
|
93
|
+
使用 BFS 遍历依赖树,visited set 防循环引用。
|
|
94
|
+
|
|
95
|
+
### Podfile.local 在 Pods 工程可见
|
|
96
|
+
|
|
97
|
+
每次 `pod install` 后,插件自动将 `Podfile.local` 加入 Pods.xcodeproj,排在工程导航栏首位,方便在 Xcode 中直接查看和编辑。
|
|
98
|
+
|
|
99
|
+
### 环境变量
|
|
100
|
+
|
|
101
|
+
| 变量 | 作用 |
|
|
102
|
+
|---|---|
|
|
103
|
+
| `COCOAPODS_LOCAL_OVERRIDE_DISABLE=1` | 禁用插件 |
|
|
104
|
+
| `COCOAPODS_LOCAL_OVERRIDE_CONFIG=/path/config.yml` | 指定自定义配置文件路径 |
|
|
105
|
+
|
|
@@ -0,0 +1,659 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# =============================================================================
|
|
4
|
+
# cocoapods-local-override — CocoaPods 本地覆盖插件
|
|
5
|
+
# =============================================================================
|
|
6
|
+
#
|
|
7
|
+
# 概述
|
|
8
|
+
# ----
|
|
9
|
+
# 在不需要修改 Podfile 的情况下,从 Podfile.local(YAML)读取配置,
|
|
10
|
+
# 将指定的 pod 从远程依赖切换为本地路径 / 替换版本号 / 替换分支。
|
|
11
|
+
#
|
|
12
|
+
# 加载机制
|
|
13
|
+
# -------
|
|
14
|
+
# CocoaPods 启动时自动扫描已安装 gem 中的 `cocoapods_plugin.rb` 并
|
|
15
|
+
# 在沙箱加载上下文执行,**不需要在 Podfile 中 require**。
|
|
16
|
+
#
|
|
17
|
+
# 三大核心功能
|
|
18
|
+
# -----------
|
|
19
|
+
# 1. pod 覆盖 — 劫持 Podfile#pod,将 local/version/branch 替换插入声明阶段。
|
|
20
|
+
# 2. 传递依赖注入 — 本地 pod 的 podspec 依赖著也在 local_pods 里时,自动注入到根 target。
|
|
21
|
+
# 3. Pods 工程可见 — post_install 将 Podfile.local 添加到 Pods.xcodeproj 导航栏首位。
|
|
22
|
+
#
|
|
23
|
+
# === 环境变量 ===
|
|
24
|
+
# COCOAPODS_LOCAL_OVERRIDE_DISABLE=1 设为 1/true/yes 时禁用所有覆盖
|
|
25
|
+
# COCOAPODS_LOCAL_OVERRIDE_CONFIG 指定自定义 YAML 配置文件路径(默认 Podfile.local)
|
|
26
|
+
|
|
27
|
+
# 依赖库:YAML 解析 / 路径处理 / 集合去重
|
|
28
|
+
require 'yaml'
|
|
29
|
+
require 'pathname'
|
|
30
|
+
require 'set'
|
|
31
|
+
|
|
32
|
+
module CocoaPodsLocalOverride
|
|
33
|
+
# ===========================================================================
|
|
34
|
+
# 模块常量
|
|
35
|
+
# ===========================================================================
|
|
36
|
+
|
|
37
|
+
# 默认配置文件名(放在 Podfile 同级目录)
|
|
38
|
+
CONFIG_FILE_NAME = 'Podfile.local'
|
|
39
|
+
# 禁用插件的环境变量名
|
|
40
|
+
ENV_DISABLE_KEY = 'COCOAPODS_LOCAL_OVERRIDE_DISABLE'
|
|
41
|
+
# 自定义配置文件路径的环境变量名
|
|
42
|
+
ENV_CONFIG_KEY = 'COCOAPODS_LOCAL_OVERRIDE_CONFIG'
|
|
43
|
+
|
|
44
|
+
# 插件自身的根目录(gem 安装根路径),用于定位模板文件
|
|
45
|
+
PLUGIN_ROOT = File.expand_path('..', __dir__).freeze
|
|
46
|
+
# 模板文件:当 Podfile.local 不存在时自动从该文件复制创建
|
|
47
|
+
EXAMPLE_FILE = 'Podfile.local.example'
|
|
48
|
+
|
|
49
|
+
# ===========================================================================
|
|
50
|
+
# 模块实例变量(相当于模块级状态)
|
|
51
|
+
# ===========================================================================
|
|
52
|
+
|
|
53
|
+
# @config — 已解析的 YAML 配置 Hash,结构:
|
|
54
|
+
# { 'local_pods' => {name => path}, 'replace_versions' => {...}, 'replace_branches' => {...} }
|
|
55
|
+
# @config_loaded — 防止重复加载配置的标记位
|
|
56
|
+
# @disabled — 缓存禁用状态(首次读取环境变量后缓存)
|
|
57
|
+
# @max_name_len — 所有 pod 名称的最大长度,用于输出对齐
|
|
58
|
+
@config = nil
|
|
59
|
+
@config_loaded = false
|
|
60
|
+
@disabled = nil
|
|
61
|
+
@max_name_len = 0
|
|
62
|
+
|
|
63
|
+
# ===========================================================================
|
|
64
|
+
# 终端 ANSI 颜色常量
|
|
65
|
+
# ===========================================================================
|
|
66
|
+
#
|
|
67
|
+
# 颜色体系:
|
|
68
|
+
# C_RESET / C_DIM — 重置与弱化(箭头、箭头两边的空格等辅助元素)
|
|
69
|
+
# C_CYAN — 主要信息展示色(pod 路径/版本号/分支名等值)
|
|
70
|
+
# C_GREEN — 成功信息([+] 前缀、配置创建等正面状态)
|
|
71
|
+
# C_YELLOW — 警告信息([x] 前缀、路径不存在等异常状态)
|
|
72
|
+
# C_MAGENTA — 未使用(保留)
|
|
73
|
+
# C_RED — 未使用(保留)
|
|
74
|
+
# LABEL_COLORS — 标签专用色:蓝绿色系渐变,视觉统一但不突兀
|
|
75
|
+
|
|
76
|
+
C_RESET = "\e[0m" # 重置所有样式
|
|
77
|
+
C_DIM = "\e[2m" # 暗淡/弱化效果(用于箭头 → 等辅助元素)
|
|
78
|
+
C_CYAN = "\e[36m" # 青色(用于显示的路径/版本/分支名等值)
|
|
79
|
+
C_GREEN = "\e[32m" # 绿色([+] 成功前缀)
|
|
80
|
+
C_YELLOW = "\e[33m" # 黄色([x] 警告前缀)
|
|
81
|
+
C_MAGENTA = "\e[35m" # 品红(保留未使用)
|
|
82
|
+
C_RED = "\e[31m" # 红色(保留未使用)
|
|
83
|
+
|
|
84
|
+
# pod 值的颜色映射(用于显示的路径/版本号/分支名等)
|
|
85
|
+
COLORS = {
|
|
86
|
+
local: C_CYAN,
|
|
87
|
+
version: C_GREEN,
|
|
88
|
+
branch: C_MAGENTA
|
|
89
|
+
}.freeze
|
|
90
|
+
|
|
91
|
+
# 标签颜色映射([ LOCAL ] / [ VERSION ] / [ BRANCH ] 颜色)
|
|
92
|
+
# 使用 256 色调色板,蓝绿色系渐变:绿→青→蓝,同色系但有区分度
|
|
93
|
+
LABEL_COLORS = {
|
|
94
|
+
local: "\e[38;5;120m", # 春绿
|
|
95
|
+
version: "\e[38;5;80m", # 青蓝
|
|
96
|
+
branch: "\e[38;5;39m" # 湖蓝
|
|
97
|
+
}.freeze
|
|
98
|
+
|
|
99
|
+
# 标签文字映射
|
|
100
|
+
LABELS = {
|
|
101
|
+
local: '[ LOCAL ]',
|
|
102
|
+
version: '[ VERSION ]',
|
|
103
|
+
branch: '[ BRANCH ]'
|
|
104
|
+
}.freeze
|
|
105
|
+
|
|
106
|
+
# ===========================================================================
|
|
107
|
+
# 环境变量检测
|
|
108
|
+
# ===========================================================================
|
|
109
|
+
|
|
110
|
+
# 检查插件是否被环境变量禁用
|
|
111
|
+
# 首次调用时读取 COCOAPODS_LOCAL_OVERRIDE_DISABLE,结果缓存到 @disabled。
|
|
112
|
+
# 支持的值: 1, true, yes(不区分大小写)
|
|
113
|
+
# @return [Boolean] true 表示已禁用
|
|
114
|
+
def self.disabled?
|
|
115
|
+
return @disabled unless @disabled.nil?
|
|
116
|
+
|
|
117
|
+
val = ENV[ENV_DISABLE_KEY]
|
|
118
|
+
# 仅当环境变量值为 1 / true / yes 时才算禁用
|
|
119
|
+
@disabled = %w[1 true yes].include?(val.to_s.downcase)
|
|
120
|
+
if @disabled
|
|
121
|
+
Pod::UI.puts "#{C_DIM}[i] cocoapods-local-override disabled by #{ENV_DISABLE_KEY}=#{val}#{C_RESET}"
|
|
122
|
+
end
|
|
123
|
+
@disabled
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# 定位配置文件路径,优先级:
|
|
127
|
+
# 1. 环境变量 COCOAPODS_LOCAL_OVERRIDE_CONFIG 指定的路径
|
|
128
|
+
# 2. 默认:Podfile 所在目录/Podfile.local
|
|
129
|
+
# 3. 兜底:当前工作目录/Podfile.local
|
|
130
|
+
# @return [String] 配置文件绝对路径
|
|
131
|
+
def self.find_config_path
|
|
132
|
+
# 优先使用环境变量指定的自定义路径
|
|
133
|
+
custom = ENV[ENV_CONFIG_KEY]
|
|
134
|
+
unless custom.to_s.strip.empty?
|
|
135
|
+
custom = custom.strip
|
|
136
|
+
if File.exist?(custom)
|
|
137
|
+
return custom
|
|
138
|
+
else
|
|
139
|
+
Pod::UI.warn "#{C_YELLOW}[x] #{ENV_CONFIG_KEY}=#{custom} not found, fallback to default#{C_RESET}"
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# 默认:从 Podfile 所在目录拼接 Podfile.local
|
|
144
|
+
podfile = begin
|
|
145
|
+
Pod::Config.instance.podfile_path
|
|
146
|
+
rescue StandardError
|
|
147
|
+
nil
|
|
148
|
+
end
|
|
149
|
+
base = podfile ? File.dirname(podfile) : Dir.pwd
|
|
150
|
+
File.join(base, CONFIG_FILE_NAME)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# ===========================================================================
|
|
154
|
+
# 自动创建配置文件
|
|
155
|
+
# ===========================================================================
|
|
156
|
+
|
|
157
|
+
# 如果 Podfile.local 不存在,从 gem 内置的 Podfile.local.example 模板
|
|
158
|
+
# 复制一份到 Podfile 同级目录,方便开发者快速上手。
|
|
159
|
+
# @param config_path [String] 目标配置文件路径
|
|
160
|
+
def self.ensure_config_file(config_path)
|
|
161
|
+
return if File.exist?(config_path)
|
|
162
|
+
|
|
163
|
+
example_path = File.join(PLUGIN_ROOT, EXAMPLE_FILE)
|
|
164
|
+
|
|
165
|
+
unless File.exist?(example_path)
|
|
166
|
+
Pod::UI.warn "#{C_YELLOW}[x] Template #{EXAMPLE_FILE} not found in gem#{C_RESET}"
|
|
167
|
+
return
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
begin
|
|
171
|
+
# 确保目标目录存在
|
|
172
|
+
FileUtils.mkdir_p(File.dirname(config_path))
|
|
173
|
+
# 复制模板文件(copy_file 保留原始文件权限)
|
|
174
|
+
FileUtils.copy_file(example_path, config_path)
|
|
175
|
+
Pod::UI.puts "#{C_GREEN}[+] Created #{File.basename(config_path)} from #{EXAMPLE_FILE}#{C_RESET}"
|
|
176
|
+
rescue StandardError => e
|
|
177
|
+
Pod::UI.warn "#{C_YELLOW}[x] Failed to create #{config_path}: #{e.message}#{C_RESET}"
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# ===========================================================================
|
|
182
|
+
# 配置加载与解析
|
|
183
|
+
# ===========================================================================
|
|
184
|
+
|
|
185
|
+
# 加载并解析 Podfile.local(YAML 格式),结果缓存到 @config。
|
|
186
|
+
# 典型配置结构:
|
|
187
|
+
# local_pods: { PodName: "../relative/path" }
|
|
188
|
+
# replace_versions: { PodName: "1.2.3" }
|
|
189
|
+
# replace_branches: { PodName: "develop-x.y.z" }
|
|
190
|
+
#
|
|
191
|
+
# @return [Hash] 解析后的配置 Hash,含 local_pods / replace_versions / replace_branches 三个 key
|
|
192
|
+
def self.load_config
|
|
193
|
+
return @config if @config_loaded
|
|
194
|
+
|
|
195
|
+
@config_loaded = true
|
|
196
|
+
|
|
197
|
+
# 环境变量禁用时直接返回空配置,不执行后续逻辑
|
|
198
|
+
if disabled?
|
|
199
|
+
@config = {}
|
|
200
|
+
return @config
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
config_path = find_config_path
|
|
204
|
+
|
|
205
|
+
# 文件不存在则自动创建模板
|
|
206
|
+
ensure_config_file(config_path)
|
|
207
|
+
|
|
208
|
+
# 创建后仍可能为空(如环境变量指定了不存在的文件)
|
|
209
|
+
unless File.exist?(config_path)
|
|
210
|
+
@config = {}
|
|
211
|
+
return @config
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# 解析 YAML,使用 safe_load(Psych 默认行为)
|
|
215
|
+
raw = YAML.load_file(config_path)
|
|
216
|
+
@config = {
|
|
217
|
+
'local_pods' => normalize_hash(raw&.dig('local_pods')),
|
|
218
|
+
'replace_versions' => normalize_hash(raw&.dig('replace_versions')),
|
|
219
|
+
'replace_branches' => normalize_hash(raw&.dig('replace_branches'))
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
# 计算所有 pod 名称的最大长度,用于输出时的列对齐
|
|
223
|
+
all_names = @config.values.flat_map(&:keys)
|
|
224
|
+
@max_name_len = all_names.map(&:length).max || 0
|
|
225
|
+
@config
|
|
226
|
+
rescue Psych::SyntaxError => e
|
|
227
|
+
Pod::UI.warn "#{C_YELLOW}[x] YAML parse error in #{File.basename(config_path)}: #{e.message}#{C_RESET}"
|
|
228
|
+
@config = {}
|
|
229
|
+
rescue StandardError => e
|
|
230
|
+
Pod::UI.warn "#{C_YELLOW}[x] Failed to load #{File.basename(config_path)}: #{e.message}#{C_RESET}"
|
|
231
|
+
@config = {}
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# ===========================================================================
|
|
235
|
+
# 配置便捷访问器
|
|
236
|
+
# ===========================================================================
|
|
237
|
+
|
|
238
|
+
# @return [Hash] 本地 pod 映射表 {name => relative_path}
|
|
239
|
+
def self.local_pods
|
|
240
|
+
load_config['local_pods']
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# @return [Hash] 版本替换映射表 {name => version_string}
|
|
244
|
+
def self.replace_versions
|
|
245
|
+
load_config['replace_versions']
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# @return [Hash] 分支替换映射表 {name => branch_name}
|
|
249
|
+
def self.replace_branches
|
|
250
|
+
load_config['replace_branches']
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
# ===========================================================================
|
|
254
|
+
# monkey-patch: 劫持 Podfile#pod 方法
|
|
255
|
+
# ===========================================================================
|
|
256
|
+
#
|
|
257
|
+
# 核心思路:
|
|
258
|
+
# 在 Podfile 解析阶段,用 alias_method 保存原始的 pod 方法,
|
|
259
|
+
# 然后用自定义方法替换它。自定义方法根据 YAML 配置决定实际调用参数:
|
|
260
|
+
# - local_pods 命中 → pod 'Name', :path => '/local/abs/path'
|
|
261
|
+
# - replace_versions → pod 'Name', 'X.Y.Z'
|
|
262
|
+
# - replace_branches → pod 'Name', :branch => 'develop-x.y.z'
|
|
263
|
+
# - 无匹配 → 原样转发给原始 pod 方法
|
|
264
|
+
#
|
|
265
|
+
# 安全措施:
|
|
266
|
+
# - @podfile_hook_applied 标记防止重复安装
|
|
267
|
+
# - method_defined? 检查防止多次 alias(幂等)
|
|
268
|
+
# - 被禁用或配置为空时跳过安装
|
|
269
|
+
|
|
270
|
+
# 拦截 Podfile#pod 方法:根据 Podfile.local 配置改写 pod 声明
|
|
271
|
+
# 在模块底部自动调用(加载即生效)
|
|
272
|
+
def self.apply_podfile_hook!
|
|
273
|
+
return if @podfile_hook_applied
|
|
274
|
+
|
|
275
|
+
# 预加载配置以触发 @max_name_len 计算
|
|
276
|
+
load_config
|
|
277
|
+
|
|
278
|
+
# 如果被禁用或配置为空,不安装钩子
|
|
279
|
+
if disabled? || all_config_empty?
|
|
280
|
+
@podfile_hook_applied = true
|
|
281
|
+
return
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
# 打开 Pod::Podfile 的单例类(class_eval),注入自定义 pod 方法
|
|
285
|
+
Pod::Podfile.class_eval do
|
|
286
|
+
# 幂等检查:如果已经 alias 过则跳过
|
|
287
|
+
unless method_defined?(:local_override_original_pod)
|
|
288
|
+
# 1. 保存原始 pod 方法
|
|
289
|
+
alias_method :local_override_original_pod, :pod
|
|
290
|
+
|
|
291
|
+
# 2. 定义新的 pod 方法,加入覆盖逻辑
|
|
292
|
+
define_method :pod do |*args|
|
|
293
|
+
pod_name = args[0].to_s
|
|
294
|
+
|
|
295
|
+
# 获取当前配置(每次调用都实时获取,但不触发重复解析)
|
|
296
|
+
local_pods = CocoaPodsLocalOverride.local_pods
|
|
297
|
+
replace_versions = CocoaPodsLocalOverride.replace_versions
|
|
298
|
+
replace_branches = CocoaPodsLocalOverride.replace_branches
|
|
299
|
+
|
|
300
|
+
# 按优先级匹配:local > version > branch > 原始调用
|
|
301
|
+
if local_pods && local_pods.key?(pod_name)
|
|
302
|
+
handle_local(pod_name, local_pods[pod_name], args)
|
|
303
|
+
elsif replace_versions && replace_versions.key?(pod_name)
|
|
304
|
+
handle_version(pod_name, replace_versions[pod_name])
|
|
305
|
+
elsif replace_branches && replace_branches.key?(pod_name)
|
|
306
|
+
handle_branch(pod_name, replace_branches[pod_name], args)
|
|
307
|
+
else
|
|
308
|
+
# 未命中任何覆盖,调用原始方法
|
|
309
|
+
local_override_original_pod(*args)
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
# =====================================================================
|
|
314
|
+
# 私有辅助方法(注入 Pod::Podfile 实例)
|
|
315
|
+
# =====================================================================
|
|
316
|
+
|
|
317
|
+
private
|
|
318
|
+
|
|
319
|
+
# 处理 local 类型覆盖:将 pod 指向本地绝对路径
|
|
320
|
+
# 路径不存在时回退到远程版本,避免构建中断
|
|
321
|
+
define_method :handle_local do |name, dev_path, original_args|
|
|
322
|
+
# 相对路径 → 基于 Podfile 目录计算绝对路径
|
|
323
|
+
abs_path = resolve_absolute_path(dev_path)
|
|
324
|
+
|
|
325
|
+
if File.directory?(abs_path)
|
|
326
|
+
# 打印覆盖信息,格式: [ LOCAL ] PodName → ../relative/path
|
|
327
|
+
Pod::UI.puts CocoaPodsLocalOverride.format_pod_line(:local, name, dev_path)
|
|
328
|
+
# 使用 :path 选项指向本地目录,:inhibit_warnings => false 保留编译警告
|
|
329
|
+
local_override_original_pod(name, :path => abs_path, :inhibit_warnings => false)
|
|
330
|
+
else
|
|
331
|
+
Pod::UI.warn "#{CocoaPodsLocalOverride::C_YELLOW}[x] path not found: #{abs_path}, use remote for #{name}#{CocoaPodsLocalOverride::C_RESET}"
|
|
332
|
+
local_override_original_pod(*original_args)
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
# 处理 version 类型覆盖:将 pod 锁定到指定版本号
|
|
337
|
+
define_method :handle_version do |name, new_version|
|
|
338
|
+
Pod::UI.puts CocoaPodsLocalOverride.format_pod_line(:version, name, new_version)
|
|
339
|
+
# 直接传递版本号字符串作为第二个位置参数
|
|
340
|
+
local_override_original_pod(name, new_version, :inhibit_warnings => false)
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
# 处理 branch 类型覆盖:将 pod 的 :branch 选项替换为目标分支
|
|
344
|
+
# 前提:原始声明中第二个参数必须是 Hash(如 pod 'X', :git => '...', :branch => '...')
|
|
345
|
+
define_method :handle_branch do |name, new_branch, args|
|
|
346
|
+
Pod::UI.puts CocoaPodsLocalOverride.format_pod_line(:branch, name, new_branch)
|
|
347
|
+
|
|
348
|
+
args_copy = args.dup
|
|
349
|
+
if args_copy[1].is_a?(Hash)
|
|
350
|
+
# 合并 :branch key,覆盖原始值
|
|
351
|
+
args_copy[1] = args_copy[1].merge(:branch => new_branch)
|
|
352
|
+
else
|
|
353
|
+
# 第二个参数不是 Hash 时无法安全替换,跳过并警告
|
|
354
|
+
Pod::UI.warn "#{CocoaPodsLocalOverride::C_YELLOW}[x] #{name}: 2nd arg not Hash, branch replace skipped#{CocoaPodsLocalOverride::C_RESET}"
|
|
355
|
+
end
|
|
356
|
+
local_override_original_pod(*args_copy)
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
# 将相对路径转换为基于 Podfile 所在目录的绝对路径
|
|
360
|
+
# 已是绝对路径则原样返回
|
|
361
|
+
define_method :resolve_absolute_path do |rel_path|
|
|
362
|
+
p = Pathname.new(rel_path)
|
|
363
|
+
return rel_path if p.absolute?
|
|
364
|
+
|
|
365
|
+
# 基准目录:Podfile 所在目录,兜底为当前工作目录
|
|
366
|
+
base = begin
|
|
367
|
+
File.dirname(Pod::Config.instance.podfile_path)
|
|
368
|
+
rescue StandardError
|
|
369
|
+
Dir.pwd
|
|
370
|
+
end
|
|
371
|
+
File.expand_path(rel_path, base)
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
@podfile_hook_applied = true
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
# ===========================================================================
|
|
380
|
+
# 工具方法
|
|
381
|
+
# ===========================================================================
|
|
382
|
+
|
|
383
|
+
# 检查所有配置项是否均为空(包括 nil 和空 Hash)
|
|
384
|
+
# 用于快速判断是否需要安装 monkey-patch,避免无配置时浪费性能
|
|
385
|
+
# @return [Boolean]
|
|
386
|
+
def self.all_config_empty?
|
|
387
|
+
cfg = load_config
|
|
388
|
+
cfg.values.all? { |v| v.nil? || v.empty? }
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
# 格式化输出一行 pod 覆盖信息,格式示例:
|
|
392
|
+
# [ LOCAL ] BatchGoodsManager → ../BusinessLayer/BatchGoodsManager
|
|
393
|
+
# [ VERSION ] MyPod → 1.2.3
|
|
394
|
+
# [ BRANCH ] DeliverGoods → develop-9.6.2
|
|
395
|
+
#
|
|
396
|
+
# @param category [Symbol] :local / :version / :branch
|
|
397
|
+
# @param name [String] pod 名称(自动对齐到 @max_name_len)
|
|
398
|
+
# @param value [String] 显示的值(路径、版本号或分支名)
|
|
399
|
+
# @param via [String, nil] 可选,传递依赖的上级 pod 名称,输出为 (via xxx)
|
|
400
|
+
# @return [String] 格式化后的 ANSI 颜色字符串
|
|
401
|
+
def self.format_pod_line(category, name, value, via: nil)
|
|
402
|
+
label = LABELS[category]
|
|
403
|
+
if via
|
|
404
|
+
via_visible = " (via #{via})"
|
|
405
|
+
name_str = "#{name}#{C_DIM}#{via_visible}#{C_RESET}"
|
|
406
|
+
pad_len = [@max_name_len - name.length - via_visible.length, 0].max
|
|
407
|
+
padded = "#{name_str}#{' ' * pad_len}"
|
|
408
|
+
else
|
|
409
|
+
padded = name.ljust(@max_name_len)
|
|
410
|
+
end
|
|
411
|
+
"#{LABEL_COLORS[category]}#{label}#{C_RESET} #{padded} #{C_DIM}→#{C_RESET} #{C_CYAN}#{value}#{C_RESET}"
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
# 标准化 YAML 中的 Hash 值:
|
|
415
|
+
# 1. 所有 key 转为 String 并 strip 空白
|
|
416
|
+
# 2. 所有 value 转为 String 并 strip 空白
|
|
417
|
+
# 3. 跳过空 key
|
|
418
|
+
# 4. nil 或非 Hash 输入返回空 {}
|
|
419
|
+
# @param raw [Object] YAML 解析后的原始值
|
|
420
|
+
# @return [Hash{String => String}]
|
|
421
|
+
def self.normalize_hash(raw)
|
|
422
|
+
return {} if raw.nil? || !raw.is_a?(Hash)
|
|
423
|
+
raw.each_with_object({}) do |(k, v), h|
|
|
424
|
+
h[k.to_s.strip] = v.to_s.strip unless k.to_s.strip.empty?
|
|
425
|
+
end
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
# ===========================================================================
|
|
429
|
+
# monkey-patch: 将 Podfile.local 加入 Pods 工程导航栏
|
|
430
|
+
# ===========================================================================
|
|
431
|
+
#
|
|
432
|
+
# 在 post_install 阶段,CocoaPods 生成并保存 .xcodeproj 后,
|
|
433
|
+
# 将 Podfile.local 作为文件引用添加到 Pods 工程导航栏的第一个位置。
|
|
434
|
+
# 这样开发者在 Xcode 中可以直接查看和编辑配置文件。
|
|
435
|
+
|
|
436
|
+
# 安装 Pods 工程钩子:拦截 create_and_save_projects 以添加 Podfile.local
|
|
437
|
+
def self.apply_pods_project_hook!
|
|
438
|
+
return if @pods_project_hook_applied
|
|
439
|
+
# 触发配置加载(可能在 Podfile 不存在时触发自动创建)
|
|
440
|
+
load_config
|
|
441
|
+
@pods_project_hook_applied = true
|
|
442
|
+
|
|
443
|
+
Pod::Installer.class_eval do
|
|
444
|
+
unless method_defined?(:local_override_original_create_and_save_projects)
|
|
445
|
+
alias_method :local_override_original_create_and_save_projects, :create_and_save_projects
|
|
446
|
+
|
|
447
|
+
# 在原始方法执行完后,额外将 Podfile.local 添加到生成的 Pods 工程中
|
|
448
|
+
define_method :create_and_save_projects do |*args|
|
|
449
|
+
local_override_original_create_and_save_projects(*args)
|
|
450
|
+
|
|
451
|
+
CocoaPodsLocalOverride.add_local_config_to_project(@generated_projects)
|
|
452
|
+
end
|
|
453
|
+
end
|
|
454
|
+
end
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
# 将 Podfile.local 添加到 Pods.xcodeproj 的主 Group 中,并移至首位
|
|
458
|
+
#
|
|
459
|
+
# @param generated_projects [Array<Xcodeproj::Project>] CocoaPods 生成的工程列表
|
|
460
|
+
def self.add_local_config_to_project(generated_projects)
|
|
461
|
+
config_path = find_config_path
|
|
462
|
+
return unless File.exist?(config_path)
|
|
463
|
+
|
|
464
|
+
# 遍历所有生成的工程(通常只有 Pods.xcodeproj 一个)
|
|
465
|
+
Array(generated_projects).each do |project|
|
|
466
|
+
next unless project
|
|
467
|
+
|
|
468
|
+
filename = File.basename(config_path)
|
|
469
|
+
|
|
470
|
+
# 检查是否已经添加过(避免重复添加)
|
|
471
|
+
already_added = project.files.any? do |f|
|
|
472
|
+
f.path == filename || f.path == config_path ||
|
|
473
|
+
(f.real_path.to_s == File.expand_path(config_path) rescue false)
|
|
474
|
+
end
|
|
475
|
+
next if already_added
|
|
476
|
+
|
|
477
|
+
# 作为文件引用添加到工程
|
|
478
|
+
project.add_podfile(config_path)
|
|
479
|
+
|
|
480
|
+
# 将 Podfile.local 移到第一个位置(排在 Podfile 前面)
|
|
481
|
+
main_group = project.main_group
|
|
482
|
+
local_ref = main_group.children.find { |f| f.path == filename }
|
|
483
|
+
|
|
484
|
+
if local_ref
|
|
485
|
+
idx = main_group.children.index(local_ref)
|
|
486
|
+
if idx && idx > 0
|
|
487
|
+
# 移除后插入到首位
|
|
488
|
+
main_group.children.delete_at(idx)
|
|
489
|
+
main_group.children.insert(0, local_ref)
|
|
490
|
+
end
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
project.save
|
|
494
|
+
Pod::UI.puts "#{C_GREEN}[+] Added #{filename} to Pods project#{C_RESET}"
|
|
495
|
+
end
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
# ===========================================================================
|
|
499
|
+
# 传递依赖自动注入
|
|
500
|
+
# ===========================================================================
|
|
501
|
+
#
|
|
502
|
+
# 问题场景:
|
|
503
|
+
# 假设 Podfile 声明了 pod 'A'(本地覆盖),而 A 的 podspec 依赖 B。
|
|
504
|
+
# 如果 B 也在 local_pods 中但 Podfile 未显式声明,则 CocoaPods 会从
|
|
505
|
+
# 远程仓库拉取 B(而非使用本地版本),导致版本不一致或本地修改不生效。
|
|
506
|
+
#
|
|
507
|
+
# 解决方案:
|
|
508
|
+
# 在 resolve_dependencies 之前,用 BFS 遍历所有已在 Podfile 中声明且
|
|
509
|
+
# 在 local_pods 中的 pod 的 podspec 依赖树。对于每个也在 local_pods 中
|
|
510
|
+
# 但未在 Podfile 中显式声明的依赖,自动通过 root_td.store_pod 注入到
|
|
511
|
+
# 根 target definition。
|
|
512
|
+
#
|
|
513
|
+
# 安全性:
|
|
514
|
+
# - visited set 防止循环依赖导致无限循环
|
|
515
|
+
# - declared_names set 跳过已声明 pod,防止重复注入
|
|
516
|
+
|
|
517
|
+
# 从本地 pod 目录中查找 podspec 文件
|
|
518
|
+
# 优先按 pod 名称查找(Name.podspec / Name.podspec.json),
|
|
519
|
+
# 回退到扫描目录下任意 podspec。
|
|
520
|
+
#
|
|
521
|
+
# @param local_path [String] pod 本地目录绝对路径
|
|
522
|
+
# @param pod_name [String] pod 名称(可选,用于精确匹配)
|
|
523
|
+
# @return [String, nil] podspec 文件路径,未找到返回 nil
|
|
524
|
+
def self.resolve_podspec_path(local_path, pod_name = nil)
|
|
525
|
+
return nil unless File.directory?(local_path)
|
|
526
|
+
|
|
527
|
+
# 优先按 pod 名称查找(更精确)
|
|
528
|
+
if pod_name && !pod_name.strip.empty?
|
|
529
|
+
[File.join(local_path, "#{pod_name}.podspec"),
|
|
530
|
+
File.join(local_path, "#{pod_name}.podspec.json")].each do |c|
|
|
531
|
+
return c if File.exist?(c)
|
|
532
|
+
end
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
# 回退:找目录下任意 .podspec(Ruby 格式优先于 JSON)
|
|
536
|
+
(Dir.glob(File.join(local_path, '*.podspec')) +
|
|
537
|
+
Dir.glob(File.join(local_path, '*.podspec.json'))).first
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
# BFS 遍历依赖树,将未声明但已在 local_pods 中的传递依赖自动注入
|
|
541
|
+
#
|
|
542
|
+
# @param podfile [Pod::Podfile] 当前 Podfile 实例
|
|
543
|
+
def self.inject_transitive_local_pods(podfile)
|
|
544
|
+
lp = local_pods
|
|
545
|
+
return if lp.nil? || lp.empty?
|
|
546
|
+
|
|
547
|
+
# 确定 Podfile 所在目录(用于计算相对路径的绝对路径)
|
|
548
|
+
podfile_dir = begin
|
|
549
|
+
File.dirname(podfile.defined_in_file || Dir.pwd)
|
|
550
|
+
rescue StandardError
|
|
551
|
+
Dir.pwd
|
|
552
|
+
end
|
|
553
|
+
|
|
554
|
+
# 收集 Podfile 中已显式声明的所有 pod 名称
|
|
555
|
+
declared_names = Set.new
|
|
556
|
+
podfile.target_definitions.each_value do |td|
|
|
557
|
+
td.non_inherited_dependencies.each { |dep| declared_names.add(dep.name) }
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
# BFS 初始化:起始队列 = 已声明且同时存在于 local_pods 中的 pod
|
|
561
|
+
visit_queue = declared_names.select { |n| lp.key?(n) }
|
|
562
|
+
visited = Set.new(visit_queue) # 已访问集合,防止循环依赖
|
|
563
|
+
|
|
564
|
+
# 获取根 target definition(Pods),后续注入都在这里进行
|
|
565
|
+
root_td = podfile.target_definitions['Pods'] || podfile.target_definitions.values.first
|
|
566
|
+
return unless root_td
|
|
567
|
+
|
|
568
|
+
# BFS 主循环
|
|
569
|
+
while (name = visit_queue.shift)
|
|
570
|
+
# 计算本地路径的绝对路径
|
|
571
|
+
abs_path = File.expand_path(lp[name], podfile_dir)
|
|
572
|
+
podspec_path = resolve_podspec_path(abs_path, name)
|
|
573
|
+
|
|
574
|
+
unless podspec_path
|
|
575
|
+
Pod::UI.warn "#{C_YELLOW}[x] #{name}: no podspec found in #{abs_path}#{C_RESET}"
|
|
576
|
+
next
|
|
577
|
+
end
|
|
578
|
+
|
|
579
|
+
# 解析 podspec 获取依赖列表
|
|
580
|
+
begin
|
|
581
|
+
spec = Pod::Specification.from_file(podspec_path)
|
|
582
|
+
rescue StandardError => e
|
|
583
|
+
Pod::UI.warn "#{C_YELLOW}[x] #{name}: failed to parse #{podspec_path} — #{e.message}#{C_RESET}"
|
|
584
|
+
next
|
|
585
|
+
end
|
|
586
|
+
|
|
587
|
+
# 遍历每个依赖
|
|
588
|
+
spec.dependencies.each do |dep|
|
|
589
|
+
# 兼容不同的依赖表示:Pod::Dependency 或 [name, requirement] 数组
|
|
590
|
+
dep_name = dep.respond_to?(:name) ? dep.name : dep.first
|
|
591
|
+
dep_name = dep_name.to_s
|
|
592
|
+
|
|
593
|
+
# 跳过不在 local_pods 中的依赖
|
|
594
|
+
next unless lp.key?(dep_name)
|
|
595
|
+
# 跳过已在 Podfile 中显式声明的依赖
|
|
596
|
+
next if declared_names.include?(dep_name)
|
|
597
|
+
# 跳过已访问过的依赖(防止循环)
|
|
598
|
+
next if visited.include?(dep_name)
|
|
599
|
+
|
|
600
|
+
dep_rel_path = lp[dep_name]
|
|
601
|
+
dep_abs_path = File.expand_path(dep_rel_path, podfile_dir)
|
|
602
|
+
|
|
603
|
+
unless File.directory?(dep_abs_path)
|
|
604
|
+
Pod::UI.warn "#{C_YELLOW}[x] #{dep_name} (dep of #{name}): path not found #{dep_abs_path}#{C_RESET}"
|
|
605
|
+
next
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
# 注入到根 target definition 中
|
|
609
|
+
root_td.store_pod(dep_name, :path => dep_abs_path, :inhibit_warnings => false)
|
|
610
|
+
declared_names.add(dep_name) # 标记为已声明,防止后续重复注入
|
|
611
|
+
visited.add(dep_name) # 标记为已访问
|
|
612
|
+
visit_queue.push(dep_name) # 加入队列继续 BFS
|
|
613
|
+
|
|
614
|
+
Pod::UI.puts CocoaPodsLocalOverride.format_pod_line(:local, dep_name, dep_rel_path, via: name)
|
|
615
|
+
end
|
|
616
|
+
end
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
# 安装传递依赖注入钩子:在 resolve_dependencies 执行前注入
|
|
620
|
+
def self.apply_transitive_dependency_hook!
|
|
621
|
+
return if @transitive_dependency_hook_applied
|
|
622
|
+
load_config
|
|
623
|
+
|
|
624
|
+
# 禁用或没有本地 pod 时跳过
|
|
625
|
+
if disabled? || local_pods.nil? || local_pods.empty?
|
|
626
|
+
@transitive_dependency_hook_applied = true
|
|
627
|
+
return
|
|
628
|
+
end
|
|
629
|
+
|
|
630
|
+
Pod::Installer.class_eval do
|
|
631
|
+
unless method_defined?(:local_override_original_resolve_deps)
|
|
632
|
+
alias_method :local_override_original_resolve_deps, :resolve_dependencies
|
|
633
|
+
|
|
634
|
+
# 在依赖解析之前注入传递依赖,确保本地版本优先被解析
|
|
635
|
+
define_method :resolve_dependencies do
|
|
636
|
+
CocoaPodsLocalOverride.inject_transitive_local_pods(podfile)
|
|
637
|
+
local_override_original_resolve_deps
|
|
638
|
+
end
|
|
639
|
+
end
|
|
640
|
+
end
|
|
641
|
+
|
|
642
|
+
@transitive_dependency_hook_applied = true
|
|
643
|
+
end
|
|
644
|
+
|
|
645
|
+
# ===========================================================================
|
|
646
|
+
# 启动入口:模块加载时自动安装所有钩子
|
|
647
|
+
# ===========================================================================
|
|
648
|
+
#
|
|
649
|
+
# 执行顺序:
|
|
650
|
+
# 1. apply_podfile_hook! — 劫持 pod() 方法(Podfile 解析阶段)
|
|
651
|
+
# 2. apply_pods_project_hook! — post_install 阶段添加 Podfile.local 到工程
|
|
652
|
+
# 3. apply_transitive_dependency_hook! — resolve_dependencies 前注入传递依赖
|
|
653
|
+
#
|
|
654
|
+
# 三个钩子各自独立,被禁用或配置为空时优雅跳过。
|
|
655
|
+
|
|
656
|
+
apply_podfile_hook!
|
|
657
|
+
apply_pods_project_hook!
|
|
658
|
+
apply_transitive_dependency_hook!
|
|
659
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: cocoapods-local-override
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- zhaocw
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-07-27 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: |2
|
|
14
|
+
自动读取项目根目录下的 Podfile.local (YAML 格式),
|
|
15
|
+
在不修改 Podfile 的情况下,将指定的 pod 从远程依赖切换为本地路径依赖。
|
|
16
|
+
也支持替换版本号和 git 分支。
|
|
17
|
+
|
|
18
|
+
Podfile.local 示例:
|
|
19
|
+
local_pods:
|
|
20
|
+
SomePod: ../path/to/SomePod
|
|
21
|
+
AnotherPod: ../path/to/AnotherPod
|
|
22
|
+
replace_versions:
|
|
23
|
+
SomePod: 1.2.3
|
|
24
|
+
replace_branches:
|
|
25
|
+
SomePod: develop
|
|
26
|
+
email:
|
|
27
|
+
executables: []
|
|
28
|
+
extensions: []
|
|
29
|
+
extra_rdoc_files: []
|
|
30
|
+
files:
|
|
31
|
+
- Podfile.local.example
|
|
32
|
+
- README.md
|
|
33
|
+
- lib/cocoapods_plugin.rb
|
|
34
|
+
homepage: https://git.code.tencent.com/zczy/ios/cocoapods-local-override
|
|
35
|
+
licenses:
|
|
36
|
+
- MIT
|
|
37
|
+
metadata: {}
|
|
38
|
+
post_install_message:
|
|
39
|
+
rdoc_options: []
|
|
40
|
+
require_paths:
|
|
41
|
+
- lib
|
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: 2.7.0
|
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
48
|
+
requirements:
|
|
49
|
+
- - ">="
|
|
50
|
+
- !ruby/object:Gem::Version
|
|
51
|
+
version: '0'
|
|
52
|
+
requirements: []
|
|
53
|
+
rubygems_version: 3.3.3
|
|
54
|
+
signing_key:
|
|
55
|
+
specification_version: 4
|
|
56
|
+
summary: 'CocoaPods plugin: override remote pods with local paths without modifying
|
|
57
|
+
Podfile.'
|
|
58
|
+
test_files: []
|