cocoapods-binary-matchup 0.0.30 → 0.0.32

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.
@@ -0,0 +1,448 @@
1
+ # encoding: UTF-8
2
+ require_relative 'helper/podfile_options'
3
+ require_relative 'tool/tool'
4
+ require_relative 'Integration_cache'
5
+
6
+ module Pod
7
+ class Podfile
8
+ module DSL
9
+
10
+ # Enable prebuiding for all pods
11
+ # it has a lower priority to other binary settings
12
+ def all_binary!
13
+ DSL.prebuild_all = true
14
+ end
15
+
16
+ # Disable simulator build
17
+ def simulator_build_disable!
18
+ DSL.simulator_build_enabled = false
19
+ end
20
+
21
+ # Enable bitcode for prebuilt frameworks
22
+ def enable_bitcode_for_prebuilt_frameworks!
23
+ DSL.bitcode_enabled = true
24
+ end
25
+
26
+ # Don't remove source code of prebuilt pods
27
+ # It may speed up the pod install if git didn't
28
+ # include the `Pods` folder
29
+ def keep_source_code_for_prebuilt_frameworks!
30
+ DSL.dont_remove_source_code = true
31
+ end
32
+
33
+
34
+ # Add custom xcodebuild option to the prebuilding action
35
+ #
36
+ # You may use this for your special demands. For example: the default archs in dSYMs
37
+ # of prebuilt frameworks is 'arm64 armv7 x86_64', and no 'i386' for 32bit simulator.
38
+ # It may generate a warning when building for a 32bit simulator. You may add following
39
+ # to your podfile
40
+ #
41
+ # ` set_custom_xcodebuild_options_for_prebuilt_frameworks :simulator => "ARCHS=$(ARCHS_STANDARD)" `
42
+ #
43
+ # Another example to disable the generating of dSYM file:
44
+ #
45
+ # ` set_custom_xcodebuild_options_for_prebuilt_frameworks "DEBUG_INFORMATION_FORMAT=dwarf"`
46
+ #
47
+ #
48
+ # @param [String or Hash] options
49
+ #
50
+ # If is a String, it will apply for device and simulator. Use it just like in the commandline.
51
+ # If is a Hash, it should be like this: { :device => "XXXXX", :simulator => "XXXXX" }
52
+ #
53
+ def set_custom_xcodebuild_options_for_prebuilt_frameworks(options)
54
+ if options.kind_of? Hash
55
+ DSL.custom_build_options = [ options[:device] ] unless options[:device].nil?
56
+ DSL.custom_build_options_simulator = [ options[:simulator] ] unless options[:simulator].nil?
57
+ elsif options.kind_of? String
58
+ DSL.custom_build_options = [options]
59
+ DSL.custom_build_options_simulator = [options]
60
+ else
61
+ raise "Wrong type."
62
+ end
63
+ end
64
+
65
+ class_attr_accessor :simulator_build_enabled
66
+ self.simulator_build_enabled = true
67
+
68
+ private
69
+ class_attr_accessor :prebuild_all
70
+ self.prebuild_all = false
71
+
72
+ class_attr_accessor :bitcode_enabled
73
+ self.bitcode_enabled = false
74
+
75
+ class_attr_accessor :dont_remove_source_code
76
+ self.dont_remove_source_code = false
77
+
78
+ class_attr_accessor :custom_build_options
79
+ class_attr_accessor :custom_build_options_simulator
80
+ self.custom_build_options = []
81
+ self.custom_build_options_simulator = []
82
+ end
83
+ end
84
+ end
85
+
86
+ Pod::HooksManager.register('cocoapods-binary-matchup', :pre_install) do |installer_context|
87
+
88
+ require_relative 'helper/feature_switches'
89
+ if Pod.is_prebuild_stage
90
+ next
91
+ end
92
+
93
+ # [Check Environment]
94
+ # check user_framework is on
95
+ podfile = installer_context.podfile
96
+ podfile.target_definition_list.each do |target_definition|
97
+ next if target_definition.prebuild_framework_names.empty?
98
+ if not target_definition.uses_frameworks?
99
+ STDERR.puts "[!] Cocoapods-binary requires `use_frameworks!`".red
100
+ exit
101
+ end
102
+ end
103
+
104
+ main_target = installer_context.podfile.target_definition_list[1]
105
+ if main_target.platform
106
+ Pod::UI.puts "📱 Project Main Target name: #{main_target.name}"
107
+ Pod.main_deployment_target_name = main_target.name
108
+ end
109
+
110
+ # -- step 1: prebuild framework ---
111
+ # Execute a sperated pod install, to generate targets for building framework,
112
+ # then compile them to framework files.
113
+ require_relative 'helper/prebuild_sandbox'
114
+ require_relative 'Prebuild'
115
+
116
+ Pod::UI.puts "🚀 Prebuild frameworks"
117
+
118
+ # Fetch original installer (which is running this pre-install hook) options,
119
+ # then pass them to our installer to perform update if needed
120
+ # Looks like this is the most appropriate way to figure out that something should be updated
121
+
122
+ update = nil
123
+ repo_update = nil
124
+
125
+ # pod install → update=false, repo_update=false
126
+ # pod update → update=true, repo_update=false
127
+ # pod install --repo-update → update=false, repo_update=true
128
+ # pod update --repo-update → update=true, repo_update=true
129
+ include ObjectSpace
130
+ ObjectSpace.each_object(Pod::Installer) { |installer|
131
+ update = installer.update
132
+ repo_update = installer.repo_update
133
+ }
134
+
135
+ # control features
136
+ Pod.is_prebuild_stage = true
137
+ Pod::Podfile::DSL.enable_prebuild_patch true # enable sikpping for prebuild targets
138
+ Pod::Installer.force_disable_integration true # don't integrate targets
139
+ Pod::Config.force_disable_write_lockfile true # disbale write lock file for perbuild podfile
140
+ Pod::Installer.disable_install_complete_message true # disable install complete message
141
+
142
+ # make another custom sandbox
143
+ standard_sandbox = installer_context.sandbox
144
+ prebuild_sandbox = Pod::PrebuildSandbox.from_standard_sandbox(standard_sandbox)
145
+
146
+ # get the podfile for prebuild
147
+ prebuild_podfile = Pod::Podfile.from_ruby(podfile.defined_in_file)
148
+
149
+ # install
150
+ lockfile = installer_context.lockfile
151
+ binary_installer = Pod::Installer.new(prebuild_sandbox, prebuild_podfile, lockfile)
152
+
153
+ # 优化的三级缓存逻辑
154
+ if binary_installer.have_exact_prebuild_cache? && !update
155
+ # 1. 优先:_prebuild 目录已有完整缓存
156
+ Pod::UI.puts "📦 Using exact prebuild cache from _Prebuild"
157
+ binary_installer.install_when_cache_hit!
158
+ else
159
+ # 3. 最后:重新构建
160
+ Pod::UI.puts "🔨 Building frameworks (no cache available)"
161
+ binary_installer.update = update
162
+ binary_installer.repo_update = repo_update
163
+ binary_installer.install!
164
+ end
165
+
166
+
167
+ # reset the environment
168
+ Pod.is_prebuild_stage = false
169
+ Pod::Installer.force_disable_integration false
170
+ Pod::Podfile::DSL.enable_prebuild_patch false
171
+ Pod::Config.force_disable_write_lockfile false
172
+ Pod::Installer.disable_install_complete_message false
173
+
174
+ # -- step 2: pod install ---
175
+ # install
176
+ Pod::UI.puts "\n"
177
+ Pod::UI.puts "🤖 Pod Install"
178
+ # 只有进入正常阶段才会执行Integration中的相关hook行为
179
+ require_relative 'Integration'
180
+ # go on the normal install step ...
181
+ end
182
+
183
+ Pod::HooksManager.register('cocoapods-binary-matchup', :post_install) do |installer_context|
184
+ require_relative 'Integration'
185
+
186
+ # 检查是否是预构建阶段,如果是则跳过
187
+ require_relative 'helper/feature_switches'
188
+ if Pod.is_prebuild_stage
189
+ next
190
+ end
191
+
192
+ Pod::UI.puts "📱 Post-install: Adding prebuilt framework resources to Pods-OMI-resources.sh"
193
+
194
+ # 从ObjectSpace中获取installer实例(类似pre_install hook的做法)
195
+ installer = nil
196
+ include ObjectSpace
197
+ ObjectSpace.each_object(Pod::Installer) { |inst| installer = inst }
198
+
199
+ if installer.nil?
200
+ Pod::UI.puts "⚠️ Could not find installer instance, skipping framework resources processing"
201
+ next
202
+ end
203
+
204
+ # 处理每个aggregate target的资源脚本
205
+ installer.aggregate_targets.each do |aggregate_target|
206
+ if !aggregate_target.static_framework?
207
+ Pod::UI.puts " ⚠️ Skipping static framework: #{aggregate_target.name}"
208
+ next
209
+ end
210
+
211
+ # 手动构建资源脚本路径(CocoaPods没有直接的script_path API)
212
+ target_support_dir = installer.sandbox.target_support_files_dir(aggregate_target.name)
213
+ script_path = target_support_dir + "#{aggregate_target.name}-resources.sh"
214
+
215
+ Pod::UI.puts " 🔍 Checking script path: #{script_path}"
216
+ next unless script_path.exist?
217
+
218
+ Pod::UI.puts " 🔧 Processing #{script_path.basename}"
219
+
220
+ # 收集预编译framework的资源
221
+ framework_resources = collect_prebuilt_framework_resources(installer, aggregate_target)
222
+ # 仅扩展 .bin(嵌套路径),不改动原有资源注入逻辑
223
+ bin_resources = collect_prebuilt_framework_bin_resources(installer, aggregate_target)
224
+
225
+ if framework_resources.any?
226
+ # 修改资源脚本
227
+ modify_resources_script(script_path, framework_resources)
228
+ Pod::UI.puts " ✅ Added #{framework_resources.count} framework resources to #{script_path.basename}"
229
+ else
230
+ Pod::UI.puts " ℹ️ No framework resources found for #{aggregate_target.name}"
231
+ end
232
+
233
+ if bin_resources.any?
234
+ append_bin_resources_script(script_path, bin_resources)
235
+ Pod::UI.puts " ✅ Added #{bin_resources.count} bin resource(s) to #{script_path.basename}"
236
+ end
237
+ end
238
+
239
+ Pod::UI.puts "✅ Framework resources processing completed"
240
+ end
241
+
242
+ # 收集预编译framework中的资源
243
+ def collect_prebuilt_framework_resources(installer, aggregate_target)
244
+ framework_resources = []
245
+
246
+ # 获取预编译的pod列表 - 强制刷新以确保获取最新状态
247
+ prebuilt_pod_names = installer.refresh_prebuild_pod_names!
248
+
249
+ aggregate_target.pod_targets.each do |pod_target|
250
+ next unless prebuilt_pod_names.include?(pod_target.pod_name)
251
+
252
+ Pod::UI.puts " 📱 Scanning prebuilt pod: #{pod_target.pod_name}"
253
+
254
+ # 获取pod目录
255
+ pod_dir = installer.sandbox.pod_dir(pod_target.pod_name)
256
+ next unless pod_dir.exist?
257
+
258
+ # 查找framework目录
259
+ framework_dirs = pod_dir.children.select { |child|
260
+ child.directory? && child.extname == '.framework'
261
+ }
262
+
263
+ framework_dirs.each do |framework_dir|
264
+ framework_name = framework_dir.basename.to_s
265
+ Pod::UI.puts " 🔍 Scanning framework: #{framework_name}"
266
+
267
+ # 扫描framework中的资源
268
+ resources = scan_framework_resources(framework_dir, pod_target.pod_name)
269
+ framework_resources.concat(resources)
270
+ end
271
+ end
272
+
273
+ framework_resources.uniq
274
+ end
275
+
276
+ # 仅收集 framework 内 .bin(支持 m/m.bin 嵌套)
277
+ def collect_prebuilt_framework_bin_resources(installer, aggregate_target)
278
+ bin_resources = []
279
+ prebuilt_pod_names = installer.refresh_prebuild_pod_names!
280
+
281
+ aggregate_target.pod_targets.each do |pod_target|
282
+ next unless prebuilt_pod_names.include?(pod_target.pod_name)
283
+
284
+ pod_dir = installer.sandbox.pod_dir(pod_target.pod_name)
285
+ next unless pod_dir.exist?
286
+
287
+ pod_dir.children.select { |c| c.directory? && c.extname == '.framework' }.each do |framework_dir|
288
+ framework_name = framework_dir.basename.to_s
289
+ Dir.glob(framework_dir.join('**/*.bin')).each do |bin_path|
290
+ rel = Pathname(bin_path).relative_path_from(framework_dir).to_s
291
+ bin_resources << {
292
+ source: "${PODS_ROOT}/#{pod_target.pod_name}/#{framework_name}/#{rel}",
293
+ relative: rel
294
+ }
295
+ end
296
+ end
297
+ end
298
+
299
+ bin_resources.uniq { |r| r[:source] }
300
+ end
301
+
302
+ # 扫描framework中的资源文件
303
+ def scan_framework_resources(framework_dir, pod_name)
304
+ resources = []
305
+ framework_name = framework_dir.basename.to_s
306
+
307
+ Pod::UI.puts " 🎯 Framework path: #{framework_dir}"
308
+
309
+ # 资源文件类型(.bin 由 append_bin_resources_script 单独处理,避免平铺路径错误)
310
+ resource_patterns = [
311
+ '*.png', '*.jpg', '*.jpeg', '*.gif', '*.webp', # 图片
312
+ '*.storyboard', '*.xib', # 界面文件
313
+ '*.storyboardc', # 编译后的storyboard
314
+ '*.nib', # 编译后的xib
315
+ '*.bundle', # 资源包
316
+ '*.ttf', '*.otf', # 字体
317
+ '*.mp3', '*.wav', '*.m4a', '*.mp4', '*.mov', # 媒体
318
+ '*.metallib' # metal or opengl文件
319
+ ]
320
+
321
+ # 扫描framework根目录
322
+ resource_patterns.each do |pattern|
323
+ Dir.glob(framework_dir + pattern).each do |resource_path|
324
+ resource_file = Pathname(resource_path)
325
+ # 其他资源使用BUILT_PRODUCTS_DIR/framework路径
326
+ # 例如:${PODS_ROOT}/CocoaDebug/CocoaDebug.framework/App.storyboardc
327
+ resource_entry = "${PODS_ROOT}/#{pod_name}/#{framework_name}/#{resource_file.basename}"
328
+ resources << resource_entry
329
+ end
330
+ end
331
+
332
+ # 扫描framework/Resources目录
333
+ resources_dir = framework_dir + 'Resources'
334
+ if resources_dir.exist?
335
+ resource_patterns.each do |pattern|
336
+ Dir.glob(resources_dir + pattern).each do |resource_path|
337
+ resource_file = Pathname(resource_path)
338
+
339
+ # Resources目录中的资源也使用BUILT_PRODUCTS_DIR路径
340
+ resource_entry = "${PODS_ROOT}/#{pod_name}/#{framework_name}/#{resource_file.basename}"
341
+ resources << resource_entry
342
+ end
343
+ end
344
+ end
345
+
346
+ resources
347
+ end
348
+
349
+ # 修改resources脚本,添加framework资源
350
+ def modify_resources_script(script_path, framework_resources)
351
+ return if framework_resources.empty?
352
+
353
+ original_content = script_path.read
354
+
355
+ # 检查是否已经添加过framework资源(避免重复)
356
+ # if original_content.include?('# Prebuilt Framework Resources')
357
+ # Pod::UI.puts " ℹ️ Framework resources already added, skipping"
358
+ # return
359
+ # end
360
+
361
+ # Pod::UI.puts " 📝 Adding #{framework_resources.count} framework resources to script"
362
+ # framework_resources.each_with_index do |resource, index|
363
+ # Pod::UI.puts " #{index + 1}. #{resource}"
364
+ # end
365
+
366
+ # 构建要添加的资源安装代码
367
+ framework_resources_block = build_framework_resources_block(framework_resources)
368
+
369
+ # 在Debug配置块中插入framework资源(而不是脚本末尾)
370
+ debug_pattern = /if \[\[ "\$CONFIGURATION" == "Debug" \]\]; then/
371
+ if original_content.match(debug_pattern)
372
+ # 找到Debug配置块,在其中插入framework资源
373
+ modified_content = original_content.sub(debug_pattern) do |match|
374
+ "#{match}\n#{framework_resources_block}"
375
+ end
376
+ else
377
+ # 如果没有找到Debug配置块,创建一个新的
378
+ framework_resources_with_condition = <<~SCRIPT
379
+ if [[ "$CONFIGURATION" == "Debug" ]]; then
380
+ #{framework_resources_block}
381
+ fi
382
+ SCRIPT
383
+
384
+ # 在脚本末尾(exit 0之前)插入
385
+ if original_content.include?('exit 0')
386
+ modified_content = original_content.sub(/exit 0/, "#{framework_resources_with_condition}\nexit 0")
387
+ else
388
+ modified_content = original_content + "\n#{framework_resources_with_condition}"
389
+ end
390
+ end
391
+
392
+ # 写回修改后的内容
393
+ script_path.write(modified_content)
394
+
395
+ Pod::UI.puts " ✅ Script modified successfully"
396
+
397
+ # 显示修改后脚本的相关部分以便验证
398
+ debug_lines = modified_content.lines.select.with_index do |line, index|
399
+ line.include?('CONFIGURATION') || line.include?('Prebuilt Framework') ||
400
+ (index > 0 && modified_content.lines[index-1].include?('CONFIGURATION'))
401
+ end
402
+ Pod::UI.puts " 📄 Debug configuration block preview:"
403
+ debug_lines.first(5).each { |line| Pod::UI.puts " #{line.chomp}" }
404
+ end
405
+
406
+ # 构建framework资源安装代码块
407
+ def build_framework_resources_block(framework_resources)
408
+ block = " # Prebuilt Framework Resources\n"
409
+ block += " echo \"Installing prebuilt framework resources...\"\n"
410
+
411
+ framework_resources.each do |resource|
412
+ block += " install_resource \"#{resource}\"\n"
413
+ end
414
+
415
+ block += " echo \"Prebuilt framework resources installation completed\"\n"
416
+
417
+ return block
418
+ end
419
+
420
+ # 仅追加 .bin 安装(保留相对路径;不改动原有 Debug 资源块)
421
+ def append_bin_resources_script(script_path, bin_resources)
422
+ return if bin_resources.empty?
423
+
424
+ content = script_path.read
425
+ content = content.gsub(
426
+ /\n?[ \t]*# Prebuilt Framework Bin Resources\n.*?echo "Prebuilt framework bin resources installation completed"\n/m,
427
+ "\n"
428
+ )
429
+
430
+ block = "# Prebuilt Framework Bin Resources\n"
431
+ block += "echo \"Installing prebuilt framework bin resources...\"\n"
432
+ bin_resources.each do |resource|
433
+ block += "PREBUILT_BIN_SRC=\"#{resource[:source]}\"\n"
434
+ block += "PREBUILT_BIN_DST=\"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/#{resource[:relative]}\"\n"
435
+ block += "mkdir -p \"$(dirname \"$PREBUILT_BIN_DST\")\"\n"
436
+ block += "if [[ -e \"$PREBUILT_BIN_SRC\" ]]; then\n"
437
+ block += " cp -f \"$PREBUILT_BIN_SRC\" \"$PREBUILT_BIN_DST\"\n"
438
+ block += "fi\n"
439
+ end
440
+ block += "echo \"Prebuilt framework bin resources installation completed\"\n"
441
+
442
+ if content.include?('exit 0')
443
+ content = content.sub(/exit 0/, "#{block}\nexit 0")
444
+ else
445
+ content = content + "\n#{block}"
446
+ end
447
+ script_path.write(content)
448
+ end