cocoapods-podgenerate 0.1.3 → 0.1.4

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.
@@ -1,18 +1,38 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # [cocoapods-podgenerate]
4
- # Monkey-patches Pod::Installer and PodsProjectGenerator for step 3/4 optimizations.
4
+ # Monkey-patches Pod::Installer PodsProjectGenerator,优化步骤 3/4
5
5
  #
6
- # v0.1.1 Optimizations:
7
- # 1. Force-enable incremental_installation + generate_multiple_pod_projects
8
- # 2. Skip project generation entirely when nothing changed
9
- # 3. Parallelize PodTargetIntegrator integration
6
+ # 优化原理:
7
+ # CocoaPods 内置了 incremental_installation generate_multiple_pod_projects
8
+ # 两个选项。本补丁强制启用这两个选项,并在其基础上进一步增强:
9
+ # - 完全无变更时跳过整个项目生成
10
+ # - 并行执行 PodTargetIntegrator
11
+ # - 并行配置 scheme 文件
12
+ # - 修复快速跳过路径的 ivars 和 hooks 缺失问题
10
13
  #
11
- # v0.1.2 Optimization:
12
- # 4. Parallelize configure_schemes across projects
14
+ # v0.1.1 优化:
15
+ # 1. 强制启用 incremental_installation + generate_multiple_pod_projects
16
+ # 2. 完全无变更时跳过项目生成
17
+ # 3. 并行化 PodTargetIntegrator 集成
13
18
  #
14
- # Reference: CocoaPods — lib/cocoapods/installer.rb
15
- # lib/cocoapods/installer/xcode/pods_project_generator.rb
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: 跳过路径不再传入空 InstallationResults,而是保留上次的 @target_installation_results
26
+ # - M1: wait_for_termination 使用 ThreadPool::DEFAULT_TIMEOUT
27
+ # - M3: integrate_targets 改用 Concurrent::FixedThreadPool(限制并发数,避免 200 线程)
28
+ #
29
+ # 线程安全保证:
30
+ # - configure_schemes: 每个 project 独立 xcodeproj → 并行安全
31
+ # - integrate_targets: 每个 PodTargetIntegrator 操作独立的 target → 并行安全
32
+ #
33
+ # 参考:CocoaPods 源码
34
+ # - lib/cocoapods/installer.rb
35
+ # - lib/cocoapods/installer/xcode/pods_project_generator.rb
16
36
 
17
37
  require 'concurrent'
18
38
  require 'etc'
@@ -22,12 +42,19 @@ module Pod
22
42
  module Patches
23
43
  module InstallerPatch
24
44
  def self.apply
25
- Pod::UI.message '[cocoapods-podgenerate] Applying InstallerPatch v3'
45
+ Pod::UI.message '[cocoapods-podgenerate] Applying InstallerPatch v4'
26
46
  Pod::Installer.prepend(ForceIncrementalInstall)
27
47
  Pod::Installer::Xcode::PodsProjectGenerator.prepend(ParallelInstall)
28
48
  end
29
49
 
30
- # ── Optimization 1: Force-enable incremental_installation ──
50
+ # ── 优化 1: 强制启用增量安装模式 ──
51
+ #
52
+ # 在 install! 入口处设置 installation_options,强制启用:
53
+ # - incremental_installation: 只重新生成有变更的 target
54
+ # - generate_multiple_pod_projects: 每个 pod 独立 xcodeproj
55
+ #
56
+ # 这两个选项是 CocoaPods 内置的(默认关闭),我们通过 monkey-patch
57
+ # 在 super 之前设置,对所有后续流程生效。
31
58
  module ForceIncrementalInstall
32
59
  def install!
33
60
  installation_options.incremental_installation = true
@@ -35,7 +62,25 @@ module Pod
35
62
  super
36
63
  end
37
64
 
38
- # ── Optimization 2: Skip project generation when nothing changed ──
65
+ # ── 优化 2: 完全无变更时跳过项目生成 + C2/H1 修复 ──
66
+ #
67
+ # 原流程即使没有任何 target 变更,create_and_save_projects 仍会被调用,
68
+ # 执行大量 file I/O 操作。本方法在 analyze_project_cache 之后检查:
69
+ # 如果 pod_targets_to_generate 和 aggregate_targets_to_generate
70
+ # 都为空(即没有任何 target 需要重新生成),则:
71
+ # 1. 跳过 create_and_save_projects(pod 项目已在磁盘上,内容未变)
72
+ # 2. 仍执行 SandboxDirCleaner(清理可能被移除的 pod 的残留文件)
73
+ # 3. 仍调用 update_project_cache(保持缓存时间戳最新)
74
+ # 4. 仍调用 run_podfile_post_install_hooks(Podfile hook 不能跳过)
75
+ #
76
+ # v0.1.4 修复 (C2):
77
+ # - 设置 @pods_project = nil, @pod_target_subprojects = [],
78
+ # @generated_projects = [](避免下游引用 nil)
79
+ # - 调用 run_podfile_post_install_hooks(之前被跳过导致 hook 静默丢失)
80
+ #
81
+ # v0.1.4 修复 (H1):
82
+ # - 使用上次的 @target_installation_results 更新缓存
83
+ # (而非空的 InstallationResults,避免清除 metadata_cache)
39
84
  def generate_pods_project
40
85
  stage_sandbox(sandbox, pod_targets)
41
86
 
@@ -45,15 +90,28 @@ module Pod
45
90
 
46
91
  if ptg.empty? && (atg.nil? || atg.empty?)
47
92
  Pod::UI.puts "[cocoapods-podgenerate] No changes — skipping project generation"
93
+
94
+ # C2 修复: 初始化所有实例变量(避免下游代码获得 nil)
48
95
  @generated_aggregate_targets = aggregate_targets
49
96
  @generated_pod_targets = []
97
+ @pods_project = nil
98
+ @pod_target_subprojects = []
99
+ @generated_projects = []
100
+
101
+ # C2 修复: 确保 post-install hooks 被调用
102
+ run_podfile_post_install_hooks
103
+
104
+ # 清理沙盒中残留的文件
50
105
  Pod::Installer::SandboxDirCleaner.new(sandbox, pod_targets, aggregate_targets).clean!
51
- update_project_cache(cache_analysis_result,
52
- Pod::Installer::Xcode::PodsProjectGenerator::InstallationResults.new({}, {}))
106
+
107
+ # H1 修复: 使用上次的安装结果(而非空结果)更新缓存
108
+ prev_results = @target_installation_results ||
109
+ Pod::Installer::Xcode::PodsProjectGenerator::InstallationResults.new({}, {})
110
+ update_project_cache(cache_analysis_result, prev_results)
53
111
  return
54
112
  end
55
113
 
56
- # Normal path
114
+ # 正常路径: 有 target 需要重新生成
57
115
  ptg.each do |pod_target|
58
116
  pod_target.build_headers.implode_path!(pod_target.headers_sandbox)
59
117
  sandbox.public_headers.implode_path!(pod_target.headers_sandbox)
@@ -65,7 +123,15 @@ module Pod
65
123
  update_project_cache(cache_analysis_result, target_installation_results)
66
124
  end
67
125
 
68
- # ── Optimization 3+4: create_and_save_projects with parallel configure_schemes ──
126
+ # ── 优化 3+4: 项目生成 + 并行 configure_schemes ──
127
+ #
128
+ # 完全覆盖原 create_and_save_projects 方法,添加并行 configure_schemes。
129
+ # 流程:
130
+ # 1. 创建 generator → 调用 generate!(并行安装 pod targets)
131
+ # 2. 设置实例变量(@pods_project 等)
132
+ # 3. UUID 预测 + 稳定化
133
+ # 4. 创建 writer → 并行清理/重建 scheme/保存
134
+ # 5. 并行 configure_schemes(每个 project 独立)
69
135
  def create_and_save_projects(pod_targets_to_generate, aggregate_targets_to_generate,
70
136
  build_configurations, project_object_version)
71
137
  UI.section 'Generating Pods project' do
@@ -77,7 +143,7 @@ module Pod
77
143
  @target_installation_results = pod_project_generation_result.target_installation_results
78
144
  @pods_project = pod_project_generation_result.project
79
145
  @pod_target_subprojects = pod_project_generation_result.projects_by_pod_targets.keys
80
- @generated_projects = ([pods_project] + pod_target_subprojects || []).compact
146
+ @generated_projects = ([pods_project] + pod_target_subprojects).compact
81
147
  @generated_pod_targets = pod_targets_to_generate
82
148
  @generated_aggregate_targets = aggregate_targets_to_generate || []
83
149
  projects_by_pod_targets = pod_project_generation_result.projects_by_pod_targets
@@ -92,7 +158,7 @@ module Pod
92
158
  run_podfile_post_install_hooks
93
159
  end
94
160
 
95
- # Parallel configure_schemes (each project is independent)
161
+ # 并行 configure_schemes(多项目时)
96
162
  pods_project_pod_targets = pod_targets_to_generate - projects_by_pod_targets.values.flatten
97
163
  all_projects_by_pod_targets = {}
98
164
  if pods_project
@@ -112,29 +178,51 @@ module Pod
112
178
 
113
179
  private
114
180
 
181
+ # 并行配置所有项目的 scheme 文件
182
+ #
183
+ # 每个 scheme 文件是独立的 .xcscheme,存储在不同 xcodeproj 的
184
+ # xcuserdata 目录中。每个 project 完全独立 → 无锁并行。
185
+ #
186
+ # v0.1.4: rescue 范围缩小到只包裹 Concurrent::FixedThreadPool 创建
115
187
  def parallel_configure_schemes(projects_by_pod_targets, generator, generation_result)
116
188
  pool_size = [[Etc.nprocessors - 1, 2].max, 16].min
117
189
  Pod::UI.message "- Configuring schemes across #{projects_by_pod_targets.size} projects (pool: #{pool_size})"
118
190
 
119
- pool = Concurrent::FixedThreadPool.new(pool_size)
120
- projects_by_pod_targets.each do |project, pts|
121
- pool.post do
191
+ pool = begin
192
+ Concurrent::FixedThreadPool.new(pool_size)
193
+ rescue NameError
194
+ nil # concurrent-ruby 不可用,回退到串行
195
+ end
196
+
197
+ if pool
198
+ projects_by_pod_targets.each do |project, pts|
199
+ pool.post do
200
+ generator.configure_schemes(project, pts, generation_result)
201
+ rescue StandardError => e
202
+ Pod::UI.warn "[cocoapods-podgenerate] Scheme configuration error: #{e.message}"
203
+ end
204
+ end
205
+ pool.shutdown
206
+ unless pool.wait_for_termination(Pod::PodGenerate::Parallel::ThreadPool::DEFAULT_TIMEOUT)
207
+ Pod::UI.warn '[cocoapods-podgenerate] Scheme configuration timed out'
208
+ pool.kill
209
+ end
210
+ else
211
+ projects_by_pod_targets.each do |project, pts|
122
212
  generator.configure_schemes(project, pts, generation_result)
123
- rescue StandardError => e
124
- Pod::UI.warn "[cocoapods-podgenerate] Scheme configuration error: #{e.message}"
125
213
  end
126
214
  end
127
- pool.shutdown
128
- pool.wait_for_termination
129
- rescue NameError
130
- # Fallback: sequential
131
- projects_by_pod_targets.each do |project, pts|
132
- generator.configure_schemes(project, pts, generation_result)
133
- end
134
215
  end
135
216
  end
136
217
 
137
- # ── Optimization 3: Parallelize PodTargetIntegrator ──
218
+ # ── 优化 3: 并行化 PodTargetIntegrator(修复 M3)──
219
+ #
220
+ # PodTargetIntegrator 为每个 pod target 添加脚本构建阶段
221
+ # (如 embed frameworks、copy resources)。每个 integrator 操作
222
+ # 独立的 target,无共享状态 → 并行安全。
223
+ #
224
+ # v0.1.4 修复 (M3): 使用 Concurrent::FixedThreadPool(限制并发数)
225
+ # 替代原来的裸 Thread.new(可能同时创建 200+ 线程导致资源耗尽)
138
226
  module ParallelInstall
139
227
  def install_pod_targets(project, pod_targets)
140
228
  super
@@ -152,18 +240,34 @@ module Pod
152
240
  return if pods_to_integrate.empty?
153
241
 
154
242
  use_io_paths = !installation_options.disable_input_output_paths
155
- threads = pods_to_integrate.map do |result|
156
- Thread.new do
157
- begin
158
- Pod::Installer::Xcode::PodsProjectGenerator::PodTargetIntegrator.new(
159
- result, :use_input_output_paths => use_io_paths
160
- ).integrate!
161
- rescue StandardError => e
162
- Pod::UI.warn "[cocoapods-podgenerate] Integrate error: #{e.message}"
163
- end
243
+
244
+ if pods_to_integrate.size <= 1
245
+ # 单 target: 直接调用,无需线程开销
246
+ Pod::Installer::Xcode::PodsProjectGenerator::PodTargetIntegrator.new(
247
+ pods_to_integrate.first, :use_input_output_paths => use_io_paths
248
+ ).integrate!
249
+ return
250
+ end
251
+
252
+ # 多 target: 使用线程池并行集成(M3 修复)
253
+ pool_size = [[Etc.nprocessors - 1, 2].max, 16].min
254
+ pool = Concurrent::FixedThreadPool.new(pool_size)
255
+
256
+ pods_to_integrate.each do |result|
257
+ pool.post do
258
+ Pod::Installer::Xcode::PodsProjectGenerator::PodTargetIntegrator.new(
259
+ result, :use_input_output_paths => use_io_paths
260
+ ).integrate!
261
+ rescue StandardError => e
262
+ Pod::UI.warn "[cocoapods-podgenerate] Integrate error: #{e.message}"
164
263
  end
165
264
  end
166
- threads.each(&:join)
265
+
266
+ pool.shutdown
267
+ unless pool.wait_for_termination(Pod::PodGenerate::Parallel::ThreadPool::DEFAULT_TIMEOUT)
268
+ Pod::UI.warn '[cocoapods-podgenerate] Target integration timed out'
269
+ pool.kill
270
+ end
167
271
  end
168
272
  end
169
273
  end
@@ -1,19 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # [cocoapods-podgenerate]
4
- # Monkey-patches MultiPodsProjectGenerator to parallelize pod target installation.
4
+ # Monkey-patches MultiPodsProjectGenerator,将 PodTarget 安装并行化。
5
5
  #
6
- # v0.1.2 Optimization:
7
- # 1. Parallel install_all_pod_targets — each pod target's install is independent I/O
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
- # Architecture:
10
- # Instead of overriding create_pods_project (which has Ruby constant resolution issues
11
- # in prepended modules for PodsProjectGenerator's inner classes), we keep the original
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
- # Reference: CocoaPods — lib/cocoapods/installer/xcode/multi_pods_project_generator.rb
16
- # lib/cocoapods/installer/xcode/pods_project_generator.rb
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
- # ── Optimization: Parallel install_all_pod_targets ──
32
- # Each project has independent pod targets (different xcodeproj directories),
33
- # so PodTargetInstaller operations can run concurrently without locking.
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
- Pod::UI.warn "[cocoapods-podgenerate] Pod target install: #{e.message}"
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 to cache pod_group lookups.
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
- # With 200+ pods and pod_group called 3-5 times per pod, this is 600-1000
9
- # linear scans of a 200-element array = ~120k-200k iterations.
6
+ # 优化原理:
7
+ # 原始 `pod_group(pod_name)` 每次调用都执行:
8
+ # pod_groups.find { |group| group.name == pod_name }
9
+ # 这是 O(n) 的线性扫描(n = pod 数量)。
10
10
  #
11
- # Fix: cache groups in a Hash for O(1) lookup. Invalidate on add_pod_group.
11
+ # 在项目生成过程中,pod_group 被调用 3-5 次/pod(从
12
+ # FileReferencesInstaller、PodTargetInstaller 的不同位置调用)。
13
+ # 对于 200+ pod 的项目,这意味着 600-1000 次线性扫描,
14
+ # 每次扫描 200 个元素 → ~120k-200k 次比较。
12
15
  #
13
- # Reference: CocoaPods source — lib/cocoapods/project.rb
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
- # Build a hash cache of pod_name => PBXGroup
26
- # Called once when first needed, then kept in sync.
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
- # Override add_pod_group to invalidate the cache
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|