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,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # [cocoapods-podgenerate]
4
- # Monkey-patches PodsProjectWriter to support incremental + parallel project saves.
4
+ # Monkey-patches PodsProjectWriter 以支持增量 + 并行项目保存。
5
5
  #
6
- # v0.1.1 Optimizations:
7
- # 1. SHA256 digest skip sort+save for unchanged projects
8
- # 2. Parallel save — multiple xcodeproj files saved in threads
6
+ # 优化原理:
7
+ # `generate_multiple_pod_projects` 启用后,每个 pod 有独立的 xcodeproj 文件,
8
+ # 每个文件的操作(清理空组、重建用户 scheme、排序、保存)天然线程安全,
9
+ # 因为不同 xcodeproj 在不同目录,不存在共享状态。
9
10
  #
10
- # v0.1.2 Optimizations:
11
- # 3. Parallel cleanup_projects — empty group removal across projects
12
- # 4. Parallel recreate_user_schemes — scheme file creation across projects
11
+ # v0.1.1 优化:
12
+ # 1. SHA256 摘要比对,跳过未变更项目的 sort+save
13
+ # 2. 多项目文件并行保存
13
14
  #
14
- # Reference: CocoaPods — lib/cocoapods/installer/xcode/pods_project_generator/pods_project_writer.rb
15
+ # v0.1.2 优化:
16
+ # 3. 并行 cleanup_projects(空 group 清理)
17
+ # 4. 并行 recreate_user_schemes(scheme 文件创建)
18
+ #
19
+ # v0.1.4 修复:
20
+ # - L1: digest_file 返回 nil 时 nil==nil 导致误跳过保存,改为只有
21
+ # 两个摘要都有效且相等时才跳过
22
+ # - M1: wait_for_termination 加 120s 超时
23
+ # - L2: 移除无效的 || [] 死代码
24
+ #
25
+ # 线程安全保证:
26
+ # - 每个 xcodeproj 是独立目录,project.save 写项目名.pbxproj
27
+ # - cleanup_single_project 只操作传入的 project 对象
28
+ # - recreate_schemes_for_project 只操作传入的 project 对象
29
+ # - results_by_native_target 在所有线程间只读共享(构建一次后不变)
30
+ #
31
+ # 参考:CocoaPods 源码
32
+ # - lib/cocoapods/installer/xcode/pods_project_generator/pods_project_writer.rb
15
33
 
34
+ require 'digest'
16
35
  require 'concurrent'
17
36
  require 'etc'
18
37
 
@@ -21,37 +40,55 @@ module Pod
21
40
  module Patches
22
41
  module ProjectWriterPatch
23
42
  def self.apply
24
- Pod::UI.message '[cocoapods-podgenerate] Applying ProjectWriterPatch v3 (incremental + parallel save + parallel write steps)'
43
+ Pod::UI.message '[cocoapods-podgenerate] Applying ProjectWriterPatch v4 (incremental + parallel save + parallel write)'
25
44
  Pod::Installer::Xcode::PodsProjectWriter.prepend(IncrementalAndParallelSave)
26
45
  end
27
46
 
28
47
  module IncrementalAndParallelSave
48
+ # 初始化:调用原始构造器后,计算所有项目的初始 SHA256 摘要
49
+ #
50
+ # @param sandbox [Sandbox]
51
+ # @param projects [Array<Project>] 所有需要管理的 Xcodeproj 项目
52
+ # @param pod_target_installation_results [Hash] pod target 安装结果
53
+ # @param installation_options [InstallationOptions]
29
54
  def initialize(sandbox, projects, pod_target_installation_results, installation_options)
30
55
  super
31
- @project_digests = {}
32
- @projects = projects
33
- @sort_needed = {}
56
+ @project_digests = {} # project.object_id => SHA256 摘要
57
+ @sort_needed = {} # project.object_id => 是否需要排序
34
58
  compute_initial_digests
35
59
  end
36
60
 
37
- # ── Optimizations 3+4+2: Parallel write! with parallel cleanup + schemes + save ──
61
+ # 并行清理、重建 scheme、然后增量保存所有项目
62
+ #
63
+ # 流程:
64
+ # 1. 并行清理每个项目的空 groups
65
+ # 2. 并行重建 scheme(将 test target 附加到 library target)
66
+ # 3. 执行 post-install hooks(yield)
67
+ # 4. SHA256 增量判断 + 并行保存
38
68
  def write!
39
- # Parallel cleanup (each project is independent)
40
69
  parallel_cleanup_projects(@projects)
41
-
42
- # Parallel recreate_user_schemes (each project is independent)
43
70
  parallel_recreate_user_schemes(@projects)
44
-
45
71
  yield if block_given?
46
-
47
72
  save_projects(@projects)
48
73
  end
49
74
 
50
- # ── Optimization 1: SHA256 skip + parallel save ──
75
+ # 增量 + 并行保存项目
76
+ #
77
+ # 流程:
78
+ # 1. 过滤: 跳过 pbxproj 内容未变的项目(SHA256 摘要比对)
79
+ # 2. 排序: 对需要保存的项目调用 sort(:groups_position => :below)
80
+ # 3. 保存: 多项目并行写入(每个 xcodeproj 独立目录)
81
+ #
82
+ # 【Bug 修复 L1】
83
+ # 只有当 old_digest 和 current_digest 都非 nil 且相等时才跳过。
84
+ # 如果任一为 nil(例如文件不可读),一律重新保存以确保项目完整性。
85
+ #
86
+ # @param projects [Array<Project>] 要保存的项目列表
51
87
  def save_projects(projects)
52
- # Filter: skip projects whose pbxproj is unchanged
53
88
  to_save = projects.select do |project|
54
- if project_unchanged?(project)
89
+ old = @project_digests[project.object_id]
90
+ cur = digest_pbxproj(project)
91
+ if old && cur && old == cur
55
92
  Pod::UI.message "- Skipping unchanged project #{UI.path project.path}"
56
93
  false
57
94
  else
@@ -60,36 +97,38 @@ module Pod
60
97
  end
61
98
  return if to_save.empty?
62
99
 
63
- # Sort each project
100
+ # 排序(串行 sort 操作轻量,并行开销反而更大)
64
101
  to_save.each { |p| p.sort(:groups_position => :below) if needs_sort?(p) }
65
102
 
66
- # Parallel save (safe: each xcodeproj is an independent directory)
103
+ # 并行保存
67
104
  if to_save.size > 1
68
105
  Pod::UI.message "- Saving #{to_save.size} projects in parallel"
69
106
  threads = to_save.map do |project|
70
107
  Thread.new do
71
- begin
72
- Pod::UI.message "- Writing Xcode project file to #{UI.path project.path}"
73
- project.save
74
- update_digest(project)
75
- rescue StandardError => e
76
- Pod::UI.warn "[cocoapods-podgenerate] Parallel save error: #{e.message}"
77
- end
108
+ Pod::UI.message "- Writing Xcode project file to #{UI.path project.path}"
109
+ project.save
110
+ update_digest(project)
111
+ rescue StandardError => e
112
+ Pod::UI.warn "[cocoapods-podgenerate] Parallel save error: #{e.message}"
78
113
  end
79
114
  end
80
115
  threads.each(&:join)
81
116
  else
82
- Pod::UI.message "- Writing Xcode project file to #{UI.path to_save.first.path}" do
83
- to_save.first.save
84
- update_digest(to_save.first)
85
- end
117
+ project = to_save.first
118
+ Pod::UI.message "- Writing Xcode project file to #{UI.path project.path}"
119
+ project.save
120
+ update_digest(project)
86
121
  end
87
122
  end
88
123
 
89
124
  private
90
125
 
91
- # ── Optimization 3: Parallel cleanup_projects ──
126
+ # ── 并行清理项目空 groups ──
92
127
 
128
+ # 移除每个项目中为空的 pods、support_files、development_pods、dependencies groups
129
+ #
130
+ # 每个 project 独立,使用 Concurrent::FixedThreadPool 并行执行。
131
+ # 如果 concurrent-ruby 不可用(NameError),回退到串行处理。
93
132
  def parallel_cleanup_projects(projects)
94
133
  pool_size = compute_pool_size
95
134
  Pod::UI.message "- Cleaning up #{projects.size} projects (pool: #{pool_size})"
@@ -97,25 +136,24 @@ module Pod
97
136
  pool = begin
98
137
  Concurrent::FixedThreadPool.new(pool_size)
99
138
  rescue NameError
100
- nil
139
+ nil # concurrent-ruby 不可用,回退到串行
101
140
  end
102
141
 
103
142
  if pool
104
143
  projects.each do |project|
105
- pool.post do
106
- cleanup_single_project(project)
107
- rescue StandardError => e
108
- Pod::UI.warn "[cocoapods-podgenerate] Cleanup error: #{e.message}"
109
- end
144
+ pool.post { cleanup_single_project(project) }
145
+ rescue StandardError => e
146
+ Pod::UI.warn "[cocoapods-podgenerate] Cleanup error: #{e.message}"
110
147
  end
111
148
  pool.shutdown
112
- pool.wait_for_termination
149
+ pool.wait_for_termination(Pod::PodGenerate::Parallel::ThreadPool::DEFAULT_TIMEOUT) || pool.kill
113
150
  else
114
- # Fallback: sequential (Concurrent not available)
151
+ # 串行回退
115
152
  projects.each { |p| cleanup_single_project(p) }
116
153
  end
117
154
  end
118
155
 
156
+ # 清理单个项目的空 groups
119
157
  def cleanup_single_project(project)
120
158
  [project.pods, project.support_files_group,
121
159
  project.development_pods, project.dependencies_group].each do |group|
@@ -123,12 +161,16 @@ module Pod
123
161
  end
124
162
  end
125
163
 
126
- # ── Optimization 4: Parallel recreate_user_schemes ──
164
+ # ── 并行重建用户 scheme ──
127
165
 
166
+ # 为所有项目重建 scheme 文件,将 test target 附加到 library target
167
+ #
168
+ # results_by_native_target 在所有线程间是只读共享缓存,
169
+ # 每个线程读取同一个 Hash 但从不修改它 → 线程安全。
128
170
  def parallel_recreate_user_schemes(projects)
129
171
  library_product_types = [:framework, :dynamic_library, :static_library]
130
172
 
131
- # Pre-build results_by_native_target once (shared read-only cache)
173
+ # 预构建 native target InstallationResult 的查找缓存(只读、线程安全)
132
174
  results_by_native_target = build_native_target_cache
133
175
 
134
176
  pool_size = compute_pool_size
@@ -144,20 +186,24 @@ module Pod
144
186
  projects.each do |project|
145
187
  pool.post do
146
188
  recreate_schemes_for_project(project, library_product_types, results_by_native_target)
147
- rescue StandardError => e
148
- Pod::UI.warn "[cocoapods-podgenerate] Scheme recreation error: #{e.message}"
149
189
  end
190
+ rescue StandardError => e
191
+ Pod::UI.warn "[cocoapods-podgenerate] Scheme recreation error: #{e.message}"
150
192
  end
151
193
  pool.shutdown
152
- pool.wait_for_termination
194
+ pool.wait_for_termination(Pod::PodGenerate::Parallel::ThreadPool::DEFAULT_TIMEOUT) || pool.kill
153
195
  else
154
- # Fallback: sequential (Concurrent not available)
155
196
  projects.each do |project|
156
197
  recreate_schemes_for_project(project, library_product_types, results_by_native_target)
157
198
  end
158
199
  end
159
200
  end
160
201
 
202
+ # 为单个项目重建 scheme
203
+ #
204
+ # @param project [Project] Xcodeproj 项目
205
+ # @param library_product_types [Array<Symbol>] 需要添加 test target 的 library 类型
206
+ # @param results_by_native_target [Hash] native_target => InstallationResult 缓存
161
207
  def recreate_schemes_for_project(project, library_product_types, results_by_native_target)
162
208
  project.recreate_user_schemes(false) do |scheme, target|
163
209
  next unless target.respond_to?(:symbol_type)
@@ -170,6 +216,7 @@ module Pod
170
216
  end
171
217
  end
172
218
 
219
+ # 构建 native_target → InstallationResult 查找缓存
173
220
  def build_native_target_cache
174
221
  cache = {}
175
222
  @pod_target_installation_results.each do |_, result|
@@ -178,56 +225,42 @@ module Pod
178
225
  cache
179
226
  end
180
227
 
181
- # ── Digest helpers (from v0.1.1) ──
228
+ # ── SHA256 摘要工具方法 ──
182
229
 
230
+ # 计算所有项目的初始 SHA256 摘要
183
231
  def compute_initial_digests
184
- @projects.each do |project|
185
- update_digest(project)
186
- end
232
+ @projects.each { |p| update_digest(p) }
187
233
  @projects.each { |p| @sort_needed[p.object_id] = true }
188
234
  end
189
235
 
190
- def project_unchanged?(project)
191
- pbx_path = pbxproj_path(project)
192
- return false unless pbx_path && File.exist?(pbx_path)
193
-
194
- old_digest = @project_digests[project.object_id]
195
- return false unless old_digest
196
-
197
- current_digest = digest_file(pbx_path)
198
- current_digest == old_digest
199
- end
200
-
236
+ # 是否需要排序(首次写入总是需要)
201
237
  def needs_sort?(project)
202
238
  @sort_needed[project.object_id] != false
203
239
  end
204
240
 
241
+ # 更新项目的 SHA256 摘要缓存
205
242
  def update_digest(project)
206
- pbx_path = pbxproj_path(project)
207
- return unless pbx_path && File.exist?(pbx_path)
208
-
209
- @project_digests[project.object_id] = digest_file(pbx_path)
243
+ digest = digest_pbxproj(project)
244
+ return unless digest
245
+ @project_digests[project.object_id] = digest
210
246
  @sort_needed[project.object_id] = false
211
247
  end
212
248
 
213
- def pbxproj_path(project)
249
+ # 计算指定项目的 pbxproj 文件的 SHA256 摘要
250
+ #
251
+ # @param project [Project] Xcodeproj 项目
252
+ # @return [String, nil] SHA256 十六进制字符串,或 nil(文件不存在/不可读)
253
+ def digest_pbxproj(project)
214
254
  path = project.path
215
255
  return nil unless path
216
- if path.to_s.end_with?('.xcodeproj')
217
- File.join(path.to_s, 'project.pbxproj')
218
- else
219
- path.to_s
220
- end
221
- end
222
-
223
- def digest_file(path)
224
- require 'digest'
225
- return nil unless File.file?(path)
226
- Digest::SHA256.file(path).hexdigest
256
+ pbx_path = path.to_s.end_with?('.xcodeproj') ? File.join(path.to_s, 'project.pbxproj') : path.to_s
257
+ return nil unless File.file?(pbx_path)
258
+ Digest::SHA256.file(pbx_path).hexdigest
227
259
  rescue StandardError
228
260
  nil
229
261
  end
230
262
 
263
+ # 计算线程池大小
231
264
  def compute_pool_size
232
265
  [[Etc.nprocessors - 1, 2].max, 16].min
233
266
  rescue NameError
@@ -31,30 +31,52 @@ module Pod
31
31
 
32
32
  module ParallelIntegration
33
33
  # ── Optimization 1: Parallel integrate_user_targets ──
34
+ # 集成用户项目中的所有 Pod target(添加构建阶段、修改配置等)。
35
+ #
36
+ # 【竞态条件修复 H2】
37
+ # 多个 AggregateTarget 可能属于同一个 Xcodeproj::Project 文件
38
+ # (例如同一个 .xcodeproj 中包含多个 native target)。
39
+ # TargetIntegrator#integrate! 会修改项目(添加 build phases、修改
40
+ # configurations),如果两个线程同时修改同一个 Xcodeproj::Project 对象,
41
+ # 就会产生竞态条件,导致 pbxproj 文件损坏。
42
+ #
43
+ # 修复策略:
44
+ # 1. 按 user_project 将所有 target 分组
45
+ # 2. 同一个项目内的 target → 串行集成(避免竞态)
46
+ # 3. 不同项目之间 → 并行集成(利用多核性能)
34
47
  def integrate_user_targets
35
48
  target_integrators = targets_to_integrate.sort_by(&:name).map do |target|
36
49
  Pod::Installer::UserProjectIntegrator::TargetIntegrator.new(target, :use_input_output_paths => use_input_output_paths?)
37
50
  end
38
51
 
39
- if target_integrators.size <= 1
40
- target_integrators.each(&:integrate!)
41
- return
42
- end
52
+ return if target_integrators.empty?
53
+ return target_integrators.each(&:integrate!) if target_integrators.size <= 1
43
54
 
44
- Pod::UI.message "- Integrating #{target_integrators.size} targets in parallel"
45
- threads = target_integrators.map do |integrator|
46
- Thread.new do
47
- begin
48
- integrator.integrate!
49
- rescue StandardError => e
50
- Pod::UI.warn "[cocoapods-podgenerate] Target integration error: #{e.message}"
55
+ # user_project 分组:同一项目的 target 共享同一个 Xcodeproj::Project 对象
56
+ groups = target_integrators.group_by { |ti| ti.send(:target).user_project }
57
+
58
+ if groups.size <= 1
59
+ # 所有 target 都属于同一项目 → 串行执行,避免竞态条件
60
+ target_integrators.each(&:integrate!)
61
+ else
62
+ # 不同项目 → 跨组并行,组内串行(同一项目内只有一个线程在修改)
63
+ Pod::UI.message "- Integrating #{target_integrators.size} targets across #{groups.size} projects in parallel"
64
+ threads = groups.map do |project, integrators|
65
+ Thread.new do
66
+ integrators.each(&:integrate!)
51
67
  end
52
68
  end
69
+ threads.each(&:join)
53
70
  end
54
- threads.each(&:join)
55
71
  end
56
72
 
57
73
  # ── Optimization 2: Parallel save_projects ──
74
+ # 保存修改后的用户项目文件。
75
+ #
76
+ # 使用并行线程保存多个 Xcodeproj 项目以提高性能。
77
+ # 脏项目调用 project.save 写入 pbxproj,非脏项目则 touch pbxproj
78
+ # 以更新文件修改时间(确保增量构建工具能正确检测变更)。
79
+ # FileUtils.touch 操作用 Mutex 保护,因为 touch 不是线程安全的。
58
80
  def save_projects(projects)
59
81
  projects = projects.uniq
60
82
 
@@ -90,7 +112,16 @@ module Pod
90
112
  end
91
113
 
92
114
  # ── Optimization 3: Parallel warn_about_xcconfig_overrides ──
93
- # Overrides the original method by prepend — called automatically from integrate!
115
+ # 检查并警告用户项目中 xcconfig 构建设置的覆盖情况。
116
+ #
117
+ # 通过 prepend 覆盖原始方法,在 integrate! 内部被自动调用。
118
+ # 使用 Concurrent::FixedThreadPool 线程池并行检查多个 target,
119
+ # 池大小由 compute_pool_size 计算(CPU 核心数 - 1,范围 2..16)。
120
+ # 如果 NameError(例如 concurrent-ruby 不可用),回退到串行执行。
121
+ #
122
+ # 注意:此方法操作的是 targets_to_integrate(AggregateTarget 数组),
123
+ # 不同 target 属于不同项目(或部分属于同一项目),但由于只是读取
124
+ # xcconfig 设置并打印警告,不修改项目,因此不存在竞态条件。
94
125
  def warn_about_xcconfig_overrides
95
126
  targets = targets_to_integrate
96
127
  return if targets.empty?
@@ -111,17 +142,32 @@ module Pod
111
142
  end
112
143
  end
113
144
  pool.shutdown
114
- pool.wait_for_termination
145
+ unless pool.wait_for_termination(Pod::PodGenerate::Parallel::ThreadPool::DEFAULT_TIMEOUT)
146
+ Pod::UI.warn '[cocoapods-podgenerate] UserIntegratorPatch: timed out waiting for xcconfig override checks'
147
+ end
115
148
  rescue NameError
116
149
  targets.each { |t| warn_single_target(t) }
117
150
  end
118
151
 
119
152
  private
120
153
 
121
- # Per-target xcconfig override check. Runs inside a thread pool slot.
122
- # NOTE: `print_override_warning` is a private method on the original
123
- # UserProjectIntegrator class. Ruby allows implicit-receiver calls to
124
- # private methods from prepended modules (no explicit `self.` prefix).
154
+ # 对单个 AggregateTarget 检查其所有 user target xcconfig 覆盖情况。
155
+ #
156
+ # 遍历逻辑:
157
+ # 1. 遍历 aggregate_target 的所有 user_target
158
+ # 2. 对每个 user_target 的每个构建配置,取出对应 xcconfig 中的设置
159
+ # 3. 比较 xcconfig 设置与当前构建设置:如果用户已在构建设置中赋值
160
+ # (非 $(inherited)),则打印覆盖警告
161
+ #
162
+ # 忽略 CODE_SIGN_IDENTITY 等特定 key(定义在 IGNORED_KEYS 中),
163
+ # 这些 key 的覆盖是预期行为,不需要警告。
164
+ #
165
+ # 在线程池的某个槽位中运行,通过 rescue 捕获异常避免单 target 错误
166
+ # 影响其他 target 的检查。
167
+ #
168
+ # 注意:print_override_warning 是原始 UserProjectIntegrator 类的
169
+ # private 方法。Ruby 允许从 prepended module 中以隐式 receiver 的
170
+ # 方式调用 private 方法(无需显式 self. 前缀)。
125
171
  def warn_single_target(aggregate_target)
126
172
  aggregate_target.user_targets.each do |user_target|
127
173
  user_target.build_configurations.each do |config|
@@ -1,5 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # [cocoapods-podgenerate]
4
+ # 插件入口文件 — 加载所有补丁和工具模块,激活优化。
5
+ #
6
+ # 加载顺序(重要—有依赖关系):
7
+ # 1. 各 patch 文件(通过 prepend monkey-patch CocoaPods 内部类)
8
+ # 2. 并行工具模块(线程池、批处理器)
9
+ # 3. 性能分析器
10
+ #
11
+ # 激活方式:
12
+ # - `plugin 'cocoapods-podgenerate'` in Podfile → CocoaPods 加载 cocoapods_plugin.rb
13
+ # → require 此文件 → 如果 Pod::HooksManager 已定义则立即 activate
14
+ # - 如果 Pod::HooksManager 尚未定义(例如加载顺序不同),
15
+ # 使用 TracePoint 延迟激活(带 500 次安全检查防止资源泄漏)
16
+ # - hooks.rb 注册了 :pre_install hook 作为兜底(如果 TracePoint 也错过了)
17
+
3
18
  require 'cocoapods-podgenerate/patches/installer_patch'
4
19
  require 'cocoapods-podgenerate/patches/project_patch'
5
20
  require 'cocoapods-podgenerate/patches/project_writer_patch'
@@ -13,8 +28,19 @@ require 'cocoapods-podgenerate/benchmark/profiler'
13
28
 
14
29
  module Pod
15
30
  module PodGenerate
31
+ # 激活所有优化补丁
32
+ #
33
+ # 幂等安全:多次调用只执行一次(@activated 守卫)。
34
+ # 所有补丁通过 Module#prepend 注入,如果重复 prepend 会导致
35
+ # 祖先链中出现重复模块,super 调用链混乱。
16
36
  def self.activate
17
- # Register all patches
37
+ return if @activated
38
+ @activated = true
39
+
40
+ # 确保 hooks 被加载(pre_install hook 作为兜底激活路径)
41
+ require_relative 'cocoapods-podgenerate/hooks'
42
+
43
+ # 按依赖顺序注册补丁
18
44
  Pod::PodGenerate::Patches::InstallerPatch.apply
19
45
  Pod::PodGenerate::Patches::ProjectPatch.apply
20
46
  Pod::PodGenerate::Patches::ProjectWriterPatch.apply
@@ -23,7 +49,7 @@ module Pod
23
49
  Pod::PodGenerate::Patches::MultiProjectGeneratorPatch.apply
24
50
  Pod::PodGenerate::Patches::CacheAnalyzerPatch.apply
25
51
 
26
- # Install hook for profiler
52
+ # 安装性能分析器钩子
27
53
  Pod::PodGenerate::Benchmark::Profiler.install
28
54
 
29
55
  Pod::UI.message '[cocoapods-podgenerate] Activated!'
@@ -31,15 +57,30 @@ module Pod
31
57
  end
32
58
  end
33
59
 
34
- # Auto-activate when loaded via `plugin` directive in Podfile
60
+ # 自动激活机制
61
+ #
62
+ # CocoaPods 的插件加载流程:
63
+ # 1. Podfile 中 `plugin 'cocoapods-podgenerate'` → CLAide 加载 cocoapods_plugin.rb
64
+ # 2. cocoapods_plugin.rb → require 'cocoapods-podgenerate' → 此文件
65
+ # 3. 此时 Pod::HooksManager 通常已由 cocoapods gem 的初始化过程定义
66
+ #
67
+ # 如果 Pod::HooksManager 已定义 → 立即激活
68
+ # 否则 → TracePoint 延迟激活,监听 :class 事件直到 HooksManager 被定义
35
69
  if defined?(Pod::HooksManager)
36
70
  Pod::PodGenerate.activate
37
71
  else
38
- # Defer activation: when CocoaPods is loaded after this file
39
- TracePoint.trace(:class) do |tp|
40
- if tp.self == Pod::HooksManager
72
+ # TracePoint 延迟激活(带安全检查)
73
+ # [:class] 事件在 Ruby 每次打开 class/module 时触发
74
+ # 500 次上限:正常情况下 HooksManager 在前 10-20 次就会被发现,
75
+ # 如果超过 500 次说明加载环境异常,及时禁用避免永久性能开销
76
+ count = 0
77
+ tp = TracePoint.trace(:class) do |tp_event|
78
+ count += 1
79
+ if tp_event.self == Pod::HooksManager
41
80
  Pod::PodGenerate.activate
42
- tp.disable
81
+ tp_event.disable
82
+ elsif count > 500
83
+ tp_event.disable # 安全阀:防止资源泄漏
43
84
  end
44
85
  end
45
86
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cocoapods-podgenerate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - PodGenerate Team