cocoapods-podgenerate 0.1.3 → 0.1.6
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 +4 -4
- data/lib/cocoapods-podgenerate/benchmark/profiler.rb +6 -4
- data/lib/cocoapods-podgenerate/command.rb +20 -5
- data/lib/cocoapods-podgenerate/parallel/batch_processor.rb +26 -25
- data/lib/cocoapods-podgenerate/parallel/thread_pool.rb +12 -21
- data/lib/cocoapods-podgenerate/patches/analyzer_patch.rb +216 -38
- data/lib/cocoapods-podgenerate/patches/cache_analyzer_patch.rb +58 -13
- data/lib/cocoapods-podgenerate/patches/installer_patch.rb +145 -34
- data/lib/cocoapods-podgenerate/patches/multi_project_generator_patch.rb +52 -16
- data/lib/cocoapods-podgenerate/patches/project_patch.rb +44 -11
- data/lib/cocoapods-podgenerate/patches/project_writer_patch.rb +113 -80
- data/lib/cocoapods-podgenerate/patches/user_integrator_patch.rb +64 -18
- data/lib/cocoapods-podgenerate.rb +48 -7
- metadata +1 -1
|
@@ -1,18 +1,41 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# [cocoapods-podgenerate]
|
|
4
|
-
# Monkey-patches Pod::Installer
|
|
4
|
+
# Monkey-patches Pod::Installer 和 PodsProjectGenerator,优化步骤 3/4。
|
|
5
5
|
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
6
|
+
# 优化原理:
|
|
7
|
+
# CocoaPods 内置了 incremental_installation 和 generate_multiple_pod_projects
|
|
8
|
+
# 两个选项。本补丁强制启用这两个选项,并在其基础上进一步增强:
|
|
9
|
+
# - 完全无变更时跳过整个项目生成
|
|
10
|
+
# - 并行执行 PodTargetIntegrator
|
|
11
|
+
# - 并行配置 scheme 文件
|
|
12
|
+
# - 修复快速跳过路径的 ivars 和 hooks 缺失问题
|
|
10
13
|
#
|
|
11
|
-
# v0.1.
|
|
12
|
-
#
|
|
14
|
+
# v0.1.1 优化:
|
|
15
|
+
# 1. 强制启用 incremental_installation + generate_multiple_pod_projects
|
|
16
|
+
# 2. 完全无变更时跳过项目生成
|
|
17
|
+
# 3. 并行化 PodTargetIntegrator 集成
|
|
13
18
|
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
19
|
+
# v0.1.2 优化:
|
|
20
|
+
# 4. 并行化 configure_schemes(跨项目)
|
|
21
|
+
#
|
|
22
|
+
# v0.1.4 修复:
|
|
23
|
+
# - C2: 快速跳过路径设置缺失的 @pods_project/@pod_target_subprojects/@generated_projects
|
|
24
|
+
# 并确保 run_podfile_post_install_hooks 被调用(即使在跳过路径上)
|
|
25
|
+
# - H1: 跳过路径不再调用 update_project_cache(@target_installation_results 在跳过路径
|
|
26
|
+
# 上始终为 nil,回退到空 InstallationResults 会清除 metadata_cache 导致下次全量重建)
|
|
27
|
+
# - L3: parallel_configure_schemes 用 defined?(Concurrent::FixedThreadPool) 替代
|
|
28
|
+
# 宽泛的 rescue NameError(NameError 可能吞掉无关的未定义常量/变量错误)
|
|
29
|
+
# - M1: wait_for_termination 使用 ThreadPool::DEFAULT_TIMEOUT
|
|
30
|
+
# - M3: integrate_targets 改用 Concurrent::FixedThreadPool(限制并发数,避免 200 线程)
|
|
31
|
+
#
|
|
32
|
+
# 线程安全保证:
|
|
33
|
+
# - configure_schemes: 每个 project 独立 xcodeproj → 并行安全
|
|
34
|
+
# - integrate_targets: 每个 PodTargetIntegrator 操作独立的 target → 并行安全
|
|
35
|
+
#
|
|
36
|
+
# 参考:CocoaPods 源码
|
|
37
|
+
# - lib/cocoapods/installer.rb
|
|
38
|
+
# - lib/cocoapods/installer/xcode/pods_project_generator.rb
|
|
16
39
|
|
|
17
40
|
require 'concurrent'
|
|
18
41
|
require 'etc'
|
|
@@ -22,12 +45,19 @@ module Pod
|
|
|
22
45
|
module Patches
|
|
23
46
|
module InstallerPatch
|
|
24
47
|
def self.apply
|
|
25
|
-
Pod::UI.message '[cocoapods-podgenerate] Applying InstallerPatch
|
|
48
|
+
Pod::UI.message '[cocoapods-podgenerate] Applying InstallerPatch v4'
|
|
26
49
|
Pod::Installer.prepend(ForceIncrementalInstall)
|
|
27
50
|
Pod::Installer::Xcode::PodsProjectGenerator.prepend(ParallelInstall)
|
|
28
51
|
end
|
|
29
52
|
|
|
30
|
-
# ──
|
|
53
|
+
# ── 优化 1: 强制启用增量安装模式 ──
|
|
54
|
+
#
|
|
55
|
+
# 在 install! 入口处设置 installation_options,强制启用:
|
|
56
|
+
# - incremental_installation: 只重新生成有变更的 target
|
|
57
|
+
# - generate_multiple_pod_projects: 每个 pod 独立 xcodeproj
|
|
58
|
+
#
|
|
59
|
+
# 这两个选项是 CocoaPods 内置的(默认关闭),我们通过 monkey-patch
|
|
60
|
+
# 在 super 之前设置,对所有后续流程生效。
|
|
31
61
|
module ForceIncrementalInstall
|
|
32
62
|
def install!
|
|
33
63
|
installation_options.incremental_installation = true
|
|
@@ -35,7 +65,25 @@ module Pod
|
|
|
35
65
|
super
|
|
36
66
|
end
|
|
37
67
|
|
|
38
|
-
# ──
|
|
68
|
+
# ── 优化 2: 完全无变更时跳过项目生成 + C2/H1 修复 ──
|
|
69
|
+
#
|
|
70
|
+
# 原流程即使没有任何 target 变更,create_and_save_projects 仍会被调用,
|
|
71
|
+
# 执行大量 file I/O 操作。本方法在 analyze_project_cache 之后检查:
|
|
72
|
+
# 如果 pod_targets_to_generate 和 aggregate_targets_to_generate
|
|
73
|
+
# 都为空(即没有任何 target 需要重新生成),则:
|
|
74
|
+
# 1. 跳过 create_and_save_projects(pod 项目已在磁盘上,内容未变)
|
|
75
|
+
# 2. 仍执行 SandboxDirCleaner(清理可能被移除的 pod 的残留文件)
|
|
76
|
+
# 3. 仍调用 update_project_cache(保持缓存时间戳最新)
|
|
77
|
+
# 4. 仍调用 run_podfile_post_install_hooks(Podfile hook 不能跳过)
|
|
78
|
+
#
|
|
79
|
+
# v0.1.4 修复 (C2):
|
|
80
|
+
# - 设置 @pods_project = nil, @pod_target_subprojects = [],
|
|
81
|
+
# @generated_projects = [](避免下游引用 nil)
|
|
82
|
+
# - 调用 run_podfile_post_install_hooks(之前被跳过导致 hook 静默丢失)
|
|
83
|
+
#
|
|
84
|
+
# v0.1.4 修复 (H1):
|
|
85
|
+
# - 使用上次的 @target_installation_results 更新缓存
|
|
86
|
+
# (而非空的 InstallationResults,避免清除 metadata_cache)
|
|
39
87
|
def generate_pods_project
|
|
40
88
|
stage_sandbox(sandbox, pod_targets)
|
|
41
89
|
|
|
@@ -45,15 +93,31 @@ module Pod
|
|
|
45
93
|
|
|
46
94
|
if ptg.empty? && (atg.nil? || atg.empty?)
|
|
47
95
|
Pod::UI.puts "[cocoapods-podgenerate] No changes — skipping project generation"
|
|
96
|
+
|
|
97
|
+
# C2 修复: 初始化所有实例变量(避免下游代码获得 nil)
|
|
48
98
|
@generated_aggregate_targets = aggregate_targets
|
|
49
99
|
@generated_pod_targets = []
|
|
100
|
+
@pods_project = nil
|
|
101
|
+
@pod_target_subprojects = []
|
|
102
|
+
@generated_projects = []
|
|
103
|
+
|
|
104
|
+
# C2 修复: 确保 post-install hooks 被调用
|
|
105
|
+
run_podfile_post_install_hooks
|
|
106
|
+
|
|
107
|
+
# 清理沙盒中残留的文件
|
|
50
108
|
Pod::Installer::SandboxDirCleaner.new(sandbox, pod_targets, aggregate_targets).clean!
|
|
51
|
-
|
|
52
|
-
|
|
109
|
+
|
|
110
|
+
# H1 修复: 当没有目标需要生成时,跳过 update_project_cache 调用
|
|
111
|
+
# @target_installation_results 仅在 create_and_save_projects 内设置,
|
|
112
|
+
# 在跳过路径上始终为 nil,回退到空 InstallationResults.new({}, {})
|
|
113
|
+
# 会导致 metadata_cache 中所有 pod target 的安装结果被清除,
|
|
114
|
+
# 下次运行时触发全量重建(cache 损坏)。
|
|
115
|
+
# 跳过路径的正确行为:不更新任何缓存,因为没有任何变化,
|
|
116
|
+
# 已有的缓存状态仍然有效。
|
|
53
117
|
return
|
|
54
118
|
end
|
|
55
119
|
|
|
56
|
-
#
|
|
120
|
+
# 正常路径: 有 target 需要重新生成
|
|
57
121
|
ptg.each do |pod_target|
|
|
58
122
|
pod_target.build_headers.implode_path!(pod_target.headers_sandbox)
|
|
59
123
|
sandbox.public_headers.implode_path!(pod_target.headers_sandbox)
|
|
@@ -65,7 +129,15 @@ module Pod
|
|
|
65
129
|
update_project_cache(cache_analysis_result, target_installation_results)
|
|
66
130
|
end
|
|
67
131
|
|
|
68
|
-
# ──
|
|
132
|
+
# ── 优化 3+4: 项目生成 + 并行 configure_schemes ──
|
|
133
|
+
#
|
|
134
|
+
# 完全覆盖原 create_and_save_projects 方法,添加并行 configure_schemes。
|
|
135
|
+
# 流程:
|
|
136
|
+
# 1. 创建 generator → 调用 generate!(并行安装 pod targets)
|
|
137
|
+
# 2. 设置实例变量(@pods_project 等)
|
|
138
|
+
# 3. UUID 预测 + 稳定化
|
|
139
|
+
# 4. 创建 writer → 并行清理/重建 scheme/保存
|
|
140
|
+
# 5. 并行 configure_schemes(每个 project 独立)
|
|
69
141
|
def create_and_save_projects(pod_targets_to_generate, aggregate_targets_to_generate,
|
|
70
142
|
build_configurations, project_object_version)
|
|
71
143
|
UI.section 'Generating Pods project' do
|
|
@@ -77,7 +149,7 @@ module Pod
|
|
|
77
149
|
@target_installation_results = pod_project_generation_result.target_installation_results
|
|
78
150
|
@pods_project = pod_project_generation_result.project
|
|
79
151
|
@pod_target_subprojects = pod_project_generation_result.projects_by_pod_targets.keys
|
|
80
|
-
@generated_projects = ([pods_project] + pod_target_subprojects
|
|
152
|
+
@generated_projects = ([pods_project] + pod_target_subprojects).compact
|
|
81
153
|
@generated_pod_targets = pod_targets_to_generate
|
|
82
154
|
@generated_aggregate_targets = aggregate_targets_to_generate || []
|
|
83
155
|
projects_by_pod_targets = pod_project_generation_result.projects_by_pod_targets
|
|
@@ -92,7 +164,7 @@ module Pod
|
|
|
92
164
|
run_podfile_post_install_hooks
|
|
93
165
|
end
|
|
94
166
|
|
|
95
|
-
#
|
|
167
|
+
# 并行 configure_schemes(多项目时)
|
|
96
168
|
pods_project_pod_targets = pod_targets_to_generate - projects_by_pod_targets.values.flatten
|
|
97
169
|
all_projects_by_pod_targets = {}
|
|
98
170
|
if pods_project
|
|
@@ -112,10 +184,28 @@ module Pod
|
|
|
112
184
|
|
|
113
185
|
private
|
|
114
186
|
|
|
187
|
+
# 并行配置所有项目的 scheme 文件
|
|
188
|
+
#
|
|
189
|
+
# 每个 scheme 文件是独立的 .xcscheme,存储在不同 xcodeproj 的
|
|
190
|
+
# xcuserdata 目录中。每个 project 完全独立 → 无锁并行。
|
|
191
|
+
#
|
|
192
|
+
# v0.1.4 修复 (L3): 使用 defined? 检查代替 rescue NameError
|
|
193
|
+
# rescue NameError 过于宽泛(捕获所有未定义常量/变量/方法错误),
|
|
194
|
+
# 可能吞掉与 Concurrent::FixedThreadPool 无关的错误。
|
|
195
|
+
# defined? 精确检查 Concurrent::FixedThreadPool 类定义是否存在。
|
|
115
196
|
def parallel_configure_schemes(projects_by_pod_targets, generator, generation_result)
|
|
116
197
|
pool_size = [[Etc.nprocessors - 1, 2].max, 16].min
|
|
117
198
|
Pod::UI.message "- Configuring schemes across #{projects_by_pod_targets.size} projects (pool: #{pool_size})"
|
|
118
199
|
|
|
200
|
+
# L3 修复: defined? 精确检查类可用性,替代 rescue NameError
|
|
201
|
+
unless defined?(Concurrent::FixedThreadPool)
|
|
202
|
+
# 回退到顺序执行(concurrent-ruby 中的 FixedThreadPool 不可用)
|
|
203
|
+
projects_by_pod_targets.each do |project, pts|
|
|
204
|
+
generator.configure_schemes(project, pts, generation_result)
|
|
205
|
+
end
|
|
206
|
+
return
|
|
207
|
+
end
|
|
208
|
+
|
|
119
209
|
pool = Concurrent::FixedThreadPool.new(pool_size)
|
|
120
210
|
projects_by_pod_targets.each do |project, pts|
|
|
121
211
|
pool.post do
|
|
@@ -125,16 +215,21 @@ module Pod
|
|
|
125
215
|
end
|
|
126
216
|
end
|
|
127
217
|
pool.shutdown
|
|
128
|
-
pool.wait_for_termination
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
projects_by_pod_targets.each do |project, pts|
|
|
132
|
-
generator.configure_schemes(project, pts, generation_result)
|
|
218
|
+
unless pool.wait_for_termination(Pod::PodGenerate::Parallel::ThreadPool::DEFAULT_TIMEOUT)
|
|
219
|
+
Pod::UI.warn '[cocoapods-podgenerate] Scheme configuration timed out'
|
|
220
|
+
pool.kill
|
|
133
221
|
end
|
|
134
222
|
end
|
|
135
223
|
end
|
|
136
224
|
|
|
137
|
-
# ──
|
|
225
|
+
# ── 优化 3: 并行化 PodTargetIntegrator(修复 M3)──
|
|
226
|
+
#
|
|
227
|
+
# PodTargetIntegrator 为每个 pod target 添加脚本构建阶段
|
|
228
|
+
# (如 embed frameworks、copy resources)。每个 integrator 操作
|
|
229
|
+
# 独立的 target,无共享状态 → 并行安全。
|
|
230
|
+
#
|
|
231
|
+
# v0.1.4 修复 (M3): 使用 Concurrent::FixedThreadPool(限制并发数)
|
|
232
|
+
# 替代原来的裸 Thread.new(可能同时创建 200+ 线程导致资源耗尽)
|
|
138
233
|
module ParallelInstall
|
|
139
234
|
def install_pod_targets(project, pod_targets)
|
|
140
235
|
super
|
|
@@ -152,18 +247,34 @@ module Pod
|
|
|
152
247
|
return if pods_to_integrate.empty?
|
|
153
248
|
|
|
154
249
|
use_io_paths = !installation_options.disable_input_output_paths
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
250
|
+
|
|
251
|
+
if pods_to_integrate.size <= 1
|
|
252
|
+
# 单 target: 直接调用,无需线程开销
|
|
253
|
+
Pod::Installer::Xcode::PodsProjectGenerator::PodTargetIntegrator.new(
|
|
254
|
+
pods_to_integrate.first, :use_input_output_paths => use_io_paths
|
|
255
|
+
).integrate!
|
|
256
|
+
return
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# 多 target: 使用线程池并行集成(M3 修复)
|
|
260
|
+
pool_size = [[Etc.nprocessors - 1, 2].max, 16].min
|
|
261
|
+
pool = Concurrent::FixedThreadPool.new(pool_size)
|
|
262
|
+
|
|
263
|
+
pods_to_integrate.each do |result|
|
|
264
|
+
pool.post do
|
|
265
|
+
Pod::Installer::Xcode::PodsProjectGenerator::PodTargetIntegrator.new(
|
|
266
|
+
result, :use_input_output_paths => use_io_paths
|
|
267
|
+
).integrate!
|
|
268
|
+
rescue StandardError => e
|
|
269
|
+
Pod::UI.warn "[cocoapods-podgenerate] Integrate error: #{e.message}"
|
|
164
270
|
end
|
|
165
271
|
end
|
|
166
|
-
|
|
272
|
+
|
|
273
|
+
pool.shutdown
|
|
274
|
+
unless pool.wait_for_termination(Pod::PodGenerate::Parallel::ThreadPool::DEFAULT_TIMEOUT)
|
|
275
|
+
Pod::UI.warn '[cocoapods-podgenerate] Target integration timed out'
|
|
276
|
+
pool.kill
|
|
277
|
+
end
|
|
167
278
|
end
|
|
168
279
|
end
|
|
169
280
|
end
|
|
@@ -1,19 +1,28 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# [cocoapods-podgenerate]
|
|
4
|
-
# Monkey-patches MultiPodsProjectGenerator
|
|
4
|
+
# Monkey-patches MultiPodsProjectGenerator,将 PodTarget 安装并行化。
|
|
5
5
|
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
6
|
+
# 优化原理:
|
|
7
|
+
# `generate_multiple_pod_projects` 启用了后,每个 pod 有自己独立的
|
|
8
|
+
# .xcodeproj 文件。原实现串行遍历所有 project,逐个调用
|
|
9
|
+
# `install_pod_targets`。由于每个 project 的 xcodeproj 目录不同,
|
|
10
|
+
# PodTargetInstaller(创建 xcconfig、module map、脚本等文件 I/O)可以
|
|
11
|
+
# 安全地并行执行,无需加锁。
|
|
8
12
|
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# project creation + file references sequential, and only parallelize the pod target
|
|
13
|
-
# installation step which is the heaviest I/O operation.
|
|
13
|
+
# v0.1.4 改进:
|
|
14
|
+
# - Bug 修复:线程池内错误不再静默吞掉,失败时传播异常避免依赖图不完整
|
|
15
|
+
# - 添加 wait_for_termination 超时(120 秒),防止死锁导致进程永久挂起
|
|
14
16
|
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
+
# 线程安全保证:
|
|
18
|
+
# - 每个 project 是独立的 Xcodeproj::Project 实例(各自 .xcodeproj 目录)
|
|
19
|
+
# - install_pod_targets 只修改传入的 project,不访问其他 project
|
|
20
|
+
# - sandbox 读取操作是线程安全的(写操作进入不同 pod 子目录)
|
|
21
|
+
# - 结果合并使用 Mutex 保护共享的 all_results Hash
|
|
22
|
+
#
|
|
23
|
+
# 参考:CocoaPods 源码
|
|
24
|
+
# - lib/cocoapods/installer/xcode/multi_pods_project_generator.rb
|
|
25
|
+
# - lib/cocoapods/installer/xcode/pods_project_generator.rb
|
|
17
26
|
|
|
18
27
|
require 'concurrent'
|
|
19
28
|
require 'etc'
|
|
@@ -22,45 +31,72 @@ module Pod
|
|
|
22
31
|
module PodGenerate
|
|
23
32
|
module Patches
|
|
24
33
|
module MultiProjectGeneratorPatch
|
|
34
|
+
# 激活补丁:对 MultiPodsProjectGenerator prepend 我们的并行版本
|
|
25
35
|
def self.apply
|
|
26
36
|
Pod::UI.message '[cocoapods-podgenerate] Applying MultiProjectGeneratorPatch (parallel pod target install)'
|
|
27
37
|
Pod::Installer::Xcode::MultiPodsProjectGenerator.prepend(ParallelMultiProjectGenerator)
|
|
28
38
|
end
|
|
29
39
|
|
|
30
40
|
module ParallelMultiProjectGenerator
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
41
|
+
# 并行安装所有 pod target
|
|
42
|
+
#
|
|
43
|
+
# 原实现(串行):
|
|
44
|
+
# projects_by_pod_targets.each_with_object({}) { |(p, pts), r| r.merge!(install_pod_targets(p, pts)) }
|
|
45
|
+
#
|
|
46
|
+
# 优化后(并行):
|
|
47
|
+
# 使用 Concurrent::FixedThreadPool,每个 project 分配一个线程
|
|
48
|
+
# 并发调用 install_pod_targets,结果通过 Mutex 合并到 all_results
|
|
49
|
+
#
|
|
50
|
+
# @param projects_by_pod_targets [Hash{Project => Array<PodTarget>}]
|
|
51
|
+
# 项目到 pod target 列表的映射
|
|
52
|
+
# @return [Hash{String => TargetInstallationResult}]
|
|
53
|
+
# pod target 名称到安装结果的映射
|
|
34
54
|
def install_all_pod_targets(projects_by_pod_targets)
|
|
35
55
|
UI.message '- Installing Pod Targets (parallel)' do
|
|
36
56
|
pool_size = compute_pool_size
|
|
37
57
|
mutex = Mutex.new
|
|
38
58
|
all_results = {}
|
|
59
|
+
errors = [] # v0.1.4: 收集错误而不是静默吞掉
|
|
39
60
|
|
|
40
61
|
pool = Concurrent::FixedThreadPool.new(pool_size)
|
|
41
62
|
projects_by_pod_targets.each do |project, pts|
|
|
42
63
|
pool.post do
|
|
64
|
+
# 每个 project 独立安装其 pod target
|
|
43
65
|
target_results = install_pod_targets(project, pts)
|
|
44
66
|
mutex.synchronize { all_results.merge!(target_results) }
|
|
45
67
|
rescue StandardError => e
|
|
46
68
|
mutex.synchronize do
|
|
47
|
-
|
|
69
|
+
errors << [project.path, e]
|
|
70
|
+
Pod::UI.warn "[cocoapods-podgenerate] Pod target install failed for #{project.path}: #{e.message}"
|
|
48
71
|
end
|
|
49
72
|
end
|
|
50
73
|
end
|
|
51
74
|
|
|
75
|
+
# v0.1.4: 带超时的等待,防止死锁
|
|
52
76
|
pool.shutdown
|
|
53
|
-
pool.wait_for_termination
|
|
77
|
+
unless pool.wait_for_termination(Pod::PodGenerate::Parallel::ThreadPool::DEFAULT_TIMEOUT)
|
|
78
|
+
Pod::UI.warn '[cocoapods-podgenerate] Pod target install timed out after 120s — forcing shutdown'
|
|
79
|
+
pool.kill
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# v0.1.4: 如果有任何失败,抛出异常让 CocoaPods 知道安装不完整
|
|
83
|
+
unless errors.empty?
|
|
84
|
+
raise Pod::Informative, "[cocoapods-podgenerate] #{errors.size} pod target(s) failed to install"
|
|
85
|
+
end
|
|
86
|
+
|
|
54
87
|
all_results
|
|
55
88
|
end
|
|
56
89
|
end
|
|
57
90
|
|
|
58
91
|
private
|
|
59
92
|
|
|
93
|
+
# 计算适合当前机器的线程池大小
|
|
94
|
+
# 使用 CPU 核心数 - 1(为主线程留一个),最小 2,最大 16
|
|
95
|
+
# @return [Integer]
|
|
60
96
|
def compute_pool_size
|
|
61
97
|
[[Etc.nprocessors - 1, 2].max, 16].min
|
|
62
98
|
rescue NameError
|
|
63
|
-
4
|
|
99
|
+
4 # Etc 不可用时的安全回退
|
|
64
100
|
end
|
|
65
101
|
end
|
|
66
102
|
end
|
|
@@ -1,44 +1,77 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# [cocoapods-podgenerate]
|
|
4
|
-
# Monkey-patches Pod::Project
|
|
5
|
-
# Original implementation does O(n) linear scan for every pod_group call:
|
|
6
|
-
# pod_groups.find { |group| group.name == pod_name }
|
|
4
|
+
# Monkey-patches Pod::Project,将 pod_group 查找从 O(n) 优化为 O(1)。
|
|
7
5
|
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
6
|
+
# 优化原理:
|
|
7
|
+
# 原始 `pod_group(pod_name)` 每次调用都执行:
|
|
8
|
+
# pod_groups.find { |group| group.name == pod_name }
|
|
9
|
+
# 这是 O(n) 的线性扫描(n = pod 数量)。
|
|
10
10
|
#
|
|
11
|
-
#
|
|
11
|
+
# 在项目生成过程中,pod_group 被调用 3-5 次/pod(从
|
|
12
|
+
# FileReferencesInstaller、PodTargetInstaller 的不同位置调用)。
|
|
13
|
+
# 对于 200+ pod 的项目,这意味着 600-1000 次线性扫描,
|
|
14
|
+
# 每次扫描 200 个元素 → ~120k-200k 次比较。
|
|
12
15
|
#
|
|
13
|
-
#
|
|
16
|
+
# 修复策略:
|
|
17
|
+
# 1. 首次调用 pod_group 时,构建 Hash 缓存(O(n) 一次性成本)
|
|
18
|
+
# 2. 后续调用直接用 cached_hash[pod_name],O(1) 查找
|
|
19
|
+
# 3. 调用 add_pod_group(添加新 pod group)时,清除缓存强制下次重建
|
|
20
|
+
# 4. 使用 `||=` 懒初始化缓存(只有第一次调用时才构建)
|
|
21
|
+
#
|
|
22
|
+
# 线程安全:
|
|
23
|
+
# 所有操作在主线程的 install! 流程中执行,无并发访问。
|
|
24
|
+
#
|
|
25
|
+
# 参考:CocoaPods 源码 — lib/cocoapods/project.rb
|
|
14
26
|
|
|
15
27
|
module Pod
|
|
16
28
|
module PodGenerate
|
|
17
29
|
module Patches
|
|
18
30
|
module ProjectPatch
|
|
31
|
+
# 激活补丁:对 Pod::Project prepend 缓存版本
|
|
19
32
|
def self.apply
|
|
20
33
|
Pod::UI.message '[cocoapods-podgenerate] Applying ProjectPatch (pod_group hash cache)'
|
|
21
34
|
Pod::Project.prepend(CachedPodGroup)
|
|
22
35
|
end
|
|
23
36
|
|
|
24
37
|
module CachedPodGroup
|
|
25
|
-
#
|
|
26
|
-
#
|
|
38
|
+
# O(1) 查找 pod_group
|
|
39
|
+
#
|
|
40
|
+
# 首次调用时通过 build_pod_group_cache 构建 Hash 缓存,
|
|
41
|
+
# 后续调用直接从缓存中查找。缓存键为 pod_name,值为 PBXGroup。
|
|
42
|
+
#
|
|
43
|
+
# @param pod_name [String] pod 名称
|
|
44
|
+
# @return [PBXGroup, nil] 对应的 group,或 nil(pod 不存在)
|
|
27
45
|
def pod_group(pod_name)
|
|
28
46
|
@pod_group_cache ||= build_pod_group_cache
|
|
29
47
|
@pod_group_cache[pod_name]
|
|
30
48
|
end
|
|
31
49
|
|
|
32
|
-
#
|
|
50
|
+
# 添加新 pod group 时清除缓存
|
|
51
|
+
#
|
|
52
|
+
# 原始方法在 main_group 或 pods/development_pods group 下
|
|
53
|
+
# 创建新的 PBXGroup。由于 project 结构发生了变化,
|
|
54
|
+
# 我们需要清除缓存,让下一次 pod_group 调用重新构建。
|
|
55
|
+
#
|
|
56
|
+
# @param pod_name [String] pod 名称
|
|
57
|
+
# @param path [String] pod 路径
|
|
58
|
+
# @param development [Boolean] 是否为开发 pod
|
|
59
|
+
# @param absolute [Boolean] 是否为绝对路径
|
|
60
|
+
# @return [PBXGroup] 新创建的 group
|
|
33
61
|
def add_pod_group(pod_name, path, development = false, absolute = false)
|
|
34
62
|
group = super
|
|
35
|
-
# Invalidate cache so the next call to pod_group rebuilds it
|
|
36
63
|
@pod_group_cache = nil if defined?(@pod_group_cache)
|
|
37
64
|
group
|
|
38
65
|
end
|
|
39
66
|
|
|
40
67
|
private
|
|
41
68
|
|
|
69
|
+
# 构建 pod_name → PBXGroup 的 Hash 缓存
|
|
70
|
+
#
|
|
71
|
+
# 遍历 pods group 和 development_pods group 下的所有子 group,
|
|
72
|
+
# 以 group.name 为键构建查找表。这是一个 O(n) 操作,但只执行一次。
|
|
73
|
+
#
|
|
74
|
+
# @return [Hash{String => PBXGroup}]
|
|
42
75
|
def build_pod_group_cache
|
|
43
76
|
cache = {}
|
|
44
77
|
pod_groups.each do |group|
|