cocoapods-binary-matchup 0.0.2 → 0.0.3
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/.gitignore +32 -0
- data/.travis.yml +23 -0
- data/Gemfile +13 -0
- data/LICENSE.txt +22 -0
- data/README.md +74 -0
- data/Rakefile +13 -0
- data/cocoapods-binary-matchup-0.0.1.gem +0 -0
- data/cocoapods-binary-matchup.gemspec +27 -0
- data/lib/cocoapods-binary-matchup/Integration.rb +293 -0
- data/lib/cocoapods-binary-matchup/Integration_cache.rb +426 -0
- data/lib/cocoapods-binary-matchup/Main.rb +171 -0
- data/lib/cocoapods-binary-matchup/Prebuild.rb +241 -0
- data/lib/cocoapods-binary-matchup/gem_version.rb +3 -0
- data/lib/cocoapods-binary-matchup/helper/feature_switches.rb +104 -0
- data/lib/cocoapods-binary-matchup/helper/passer.rb +41 -0
- data/lib/cocoapods-binary-matchup/helper/podfile_options.rb +120 -0
- data/lib/cocoapods-binary-matchup/helper/prebuild_sandbox.rb +53 -0
- data/lib/cocoapods-binary-matchup/helper/target_checker.rb +49 -0
- data/lib/cocoapods-binary-matchup/rome/build_framework.rb +226 -0
- data/lib/cocoapods-binary-matchup/tool/tool.rb +12 -0
- data/lib/cocoapods-binary.rb +1 -0
- data/lib/cocoapods_plugin.rb +2 -0
- data/spec/spec_helper.rb +50 -0
- metadata +27 -3
@@ -0,0 +1,426 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'digest'
|
3
|
+
|
4
|
+
module Pod
|
5
|
+
module PrebuildCache
|
6
|
+
class Cache
|
7
|
+
|
8
|
+
# 获取缓存根目录
|
9
|
+
def self.cache_root_dir
|
10
|
+
cache_dir = File.expand_path("~/.PodCache")
|
11
|
+
FileUtils.mkdir_p(cache_dir) unless Dir.exist?(cache_dir)
|
12
|
+
cache_dir
|
13
|
+
end
|
14
|
+
|
15
|
+
# 获取特定pod的缓存目录
|
16
|
+
def self.cache_dir_for_pod(pod_name, version)
|
17
|
+
File.join(cache_root_dir, pod_name, version)
|
18
|
+
end
|
19
|
+
|
20
|
+
# 获取pod的版本信息,优先使用commit hash
|
21
|
+
def self.get_pod_version(sandbox, pod_name)
|
22
|
+
begin
|
23
|
+
Pod::UI.puts "🔍 Looking for version of: #{pod_name}"
|
24
|
+
# 优先级:commit > tag > version > timestamp
|
25
|
+
|
26
|
+
# 1. 首先尝试从Manifest.lock获取commit信息
|
27
|
+
manifest_lock = sandbox.root + 'Manifest.lock'
|
28
|
+
if manifest_lock.exist?
|
29
|
+
manifest_content = YAML.load_file(manifest_lock)
|
30
|
+
|
31
|
+
# 优先检查 CHECKOUT OPTIONS 中的commit信息
|
32
|
+
checkout_options = manifest_content['CHECKOUT OPTIONS'] || {}
|
33
|
+
if checkout_options[pod_name].is_a?(Hash)
|
34
|
+
checkout_info = checkout_options[pod_name]
|
35
|
+
# 优先使用commit,然后是tag
|
36
|
+
if checkout_info[:commit] || checkout_info['commit']
|
37
|
+
commit = checkout_info[:commit] || checkout_info['commit']
|
38
|
+
Pod::UI.puts "📋 Using commit for #{pod_name}: #{commit[0..7]}..."
|
39
|
+
return commit
|
40
|
+
elsif checkout_info[:tag] || checkout_info['tag']
|
41
|
+
tag = checkout_info[:tag] || checkout_info['tag']
|
42
|
+
Pod::UI.puts "📋 Using tag for #{pod_name}: #{tag}"
|
43
|
+
return tag
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# 如果没有commit信息,查找版本信息
|
48
|
+
pods = manifest_content['PODS'] || []
|
49
|
+
Pod::UI.puts "🔍 Searching in #{pods.length} pod entries from Manifest.lock"
|
50
|
+
|
51
|
+
pods.each do |pod_entry|
|
52
|
+
if pod_entry.is_a?(Hash)
|
53
|
+
# Hash 格式:{"PodName (1.0.0)" => [...]}
|
54
|
+
pod_entry.each do |key, _|
|
55
|
+
key_str = key.to_s
|
56
|
+
Pod::UI.puts " 📦 Checking hash key: #{key_str}"
|
57
|
+
|
58
|
+
# 使用更灵活的匹配逻辑,支持subspec
|
59
|
+
match = key_str.match(/^#{Regexp.escape(pod_name)}\s*\(([^)]+)\)/)
|
60
|
+
if match
|
61
|
+
Pod::UI.puts "📋 Found exact match! Using version for #{pod_name}: #{match[1]}"
|
62
|
+
return match[1]
|
63
|
+
end
|
64
|
+
|
65
|
+
# 如果精确匹配失败,尝试检查是否是subspec的情况
|
66
|
+
# 例如:pod_name是"GoogleUtilities/AppDelegateSwizzler",但key是"GoogleUtilities/AppDelegateSwizzler (8.1.0)"
|
67
|
+
if key_str.include?(pod_name) && key_str.include?('(')
|
68
|
+
version_match = key_str.match(/\(([^)]+)\)/)
|
69
|
+
if version_match
|
70
|
+
Pod::UI.puts "📋 Found partial match! Using version for #{pod_name}: #{version_match[1]}"
|
71
|
+
return version_match[1]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
elsif pod_entry.is_a?(String)
|
76
|
+
# String 格式:"PodName (1.0.0)"
|
77
|
+
Pod::UI.puts " 📦 Checking string: #{pod_entry}"
|
78
|
+
|
79
|
+
match = pod_entry.match(/^#{Regexp.escape(pod_name)}\s*\(([^)]+)\)/)
|
80
|
+
if match
|
81
|
+
Pod::UI.puts "📋 Found exact match! Using version for #{pod_name}: #{match[1]}"
|
82
|
+
return match[1]
|
83
|
+
end
|
84
|
+
|
85
|
+
# 如果精确匹配失败,尝试部分匹配
|
86
|
+
if pod_entry.include?(pod_name) && pod_entry.include?('(')
|
87
|
+
version_match = pod_entry.match(/\(([^)]+)\)/)
|
88
|
+
if version_match
|
89
|
+
Pod::UI.puts "📋 Found partial match! Using version for #{pod_name}: #{version_match[1]}"
|
90
|
+
return version_match[1]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
Pod::UI.puts "⚠️ No version found for #{pod_name} in Manifest.lock"
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
|
101
|
+
# 2. 如果Manifest.lock不存在或没找到,尝试从Podfile.lock获取
|
102
|
+
podfile_lock = sandbox.root.parent + 'Podfile.lock'
|
103
|
+
if podfile_lock.exist?
|
104
|
+
podfile_content = YAML.load_file(podfile_lock)
|
105
|
+
|
106
|
+
# 同样优先检查checkout信息
|
107
|
+
checkout_options = podfile_content['CHECKOUT OPTIONS'] || {}
|
108
|
+
if checkout_options[pod_name].is_a?(Hash)
|
109
|
+
checkout_info = checkout_options[pod_name]
|
110
|
+
if checkout_info[:commit] || checkout_info['commit']
|
111
|
+
commit = checkout_info[:commit] || checkout_info['commit']
|
112
|
+
Pod::UI.puts "📋 Using commit for #{pod_name}: #{commit[0..7]}..."
|
113
|
+
return commit
|
114
|
+
elsif checkout_info[:tag] || checkout_info['tag']
|
115
|
+
tag = checkout_info[:tag] || checkout_info['tag']
|
116
|
+
Pod::UI.puts "📋 Using tag for #{pod_name}: #{tag}"
|
117
|
+
return tag
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# 查找版本信息
|
122
|
+
pods = podfile_content['PODS'] || []
|
123
|
+
Pod::UI.puts "🔍 Searching in #{pods.length} pod entries from Podfile.lock"
|
124
|
+
|
125
|
+
pods.each do |pod_entry|
|
126
|
+
if pod_entry.is_a?(Hash)
|
127
|
+
pod_entry.each do |key, _|
|
128
|
+
key_str = key.to_s
|
129
|
+
Pod::UI.puts " 📦 Checking hash key: #{key_str}"
|
130
|
+
|
131
|
+
# 使用更灵活的匹配逻辑,支持subspec
|
132
|
+
match = key_str.match(/^#{Regexp.escape(pod_name)}\s*\(([^)]+)\)/)
|
133
|
+
if match
|
134
|
+
Pod::UI.puts "📋 Found exact match! Using version for #{pod_name}: #{match[1]}"
|
135
|
+
return match[1]
|
136
|
+
end
|
137
|
+
|
138
|
+
# 如果精确匹配失败,尝试部分匹配
|
139
|
+
if key_str.include?(pod_name) && key_str.include?('(')
|
140
|
+
version_match = key_str.match(/\(([^)]+)\)/)
|
141
|
+
if version_match
|
142
|
+
Pod::UI.puts "📋 Found partial match! Using version for #{pod_name}: #{version_match[1]}"
|
143
|
+
return version_match[1]
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
elsif pod_entry.is_a?(String)
|
148
|
+
# String 格式:"PodName (1.0.0)"
|
149
|
+
Pod::UI.puts " 📦 Checking string: #{pod_entry}"
|
150
|
+
|
151
|
+
match = pod_entry.match(/^#{Regexp.escape(pod_name)}\s*\(([^)]+)\)/)
|
152
|
+
if match
|
153
|
+
Pod::UI.puts "📋 Found exact match! Using version for #{pod_name}: #{match[1]}"
|
154
|
+
return match[1]
|
155
|
+
end
|
156
|
+
|
157
|
+
# 如果精确匹配失败,尝试部分匹配
|
158
|
+
if pod_entry.include?(pod_name) && pod_entry.include?('(')
|
159
|
+
version_match = pod_entry.match(/\(([^)]+)\)/)
|
160
|
+
if version_match
|
161
|
+
Pod::UI.puts "📋 Found partial match! Using version for #{pod_name}: #{version_match[1]}"
|
162
|
+
return version_match[1]
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
Pod::UI.puts "⚠️ No version found for #{pod_name} in Podfile.lock"
|
169
|
+
end
|
170
|
+
|
171
|
+
# 3. 如果无法从任何lockfile获取,使用时间戳作为版本
|
172
|
+
timestamp = Time.now.strftime("%Y%m%d_%H%M%S")
|
173
|
+
Pod::UI.puts "⚠️ Warning: Cannot find version/commit for #{pod_name} in lock files, using timestamp: #{timestamp}"
|
174
|
+
return timestamp
|
175
|
+
rescue => e
|
176
|
+
timestamp = Time.now.strftime("%Y%m%d_%H%M%S")
|
177
|
+
Pod::UI.puts "⚠️ Warning: Error getting version for #{pod_name}: #{e.message}, using timestamp: #{timestamp}"
|
178
|
+
return timestamp
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# 计算目录内容的哈希值(用于验证缓存完整性)
|
183
|
+
def self.calculate_dir_hash(dir_path)
|
184
|
+
return nil unless Dir.exist?(dir_path)
|
185
|
+
|
186
|
+
files = Dir.glob("#{dir_path}/**/*", File::FNM_DOTMATCH).select { |f| File.file?(f) }
|
187
|
+
files.sort!
|
188
|
+
|
189
|
+
hasher = Digest::SHA256.new
|
190
|
+
files.each do |file|
|
191
|
+
# 添加文件路径和修改时间到哈希计算中
|
192
|
+
relative_path = file.sub("#{dir_path}/", "")
|
193
|
+
hasher.update(relative_path)
|
194
|
+
hasher.update(File.mtime(file).to_s)
|
195
|
+
end
|
196
|
+
|
197
|
+
hasher.hexdigest
|
198
|
+
end
|
199
|
+
|
200
|
+
# 检查缓存是否存在
|
201
|
+
def self.cache_exists?(pod_name, version)
|
202
|
+
cache_dir = cache_dir_for_pod(pod_name, version)
|
203
|
+
Dir.exist?(cache_dir) && !Dir.empty?(cache_dir)
|
204
|
+
end
|
205
|
+
|
206
|
+
# 检查缓存是否存在且有效(用于验证完整性)
|
207
|
+
def self.cache_valid?(pod_name, version, source_dir = nil)
|
208
|
+
cache_dir = cache_dir_for_pod(pod_name, version)
|
209
|
+
return false unless Dir.exist?(cache_dir)
|
210
|
+
|
211
|
+
# 如果没有提供源目录,只检查缓存是否存在
|
212
|
+
return true if source_dir.nil?
|
213
|
+
|
214
|
+
# 检查缓存的哈希文件
|
215
|
+
hash_file = File.join(cache_dir, '.cache_hash')
|
216
|
+
return false unless File.exist?(hash_file)
|
217
|
+
|
218
|
+
begin
|
219
|
+
cached_hash = File.read(hash_file).strip
|
220
|
+
current_hash = calculate_dir_hash(source_dir)
|
221
|
+
|
222
|
+
return cached_hash == current_hash
|
223
|
+
rescue => e
|
224
|
+
Pod::UI.puts "⚠️ Warning: Cannot validate cache for #{pod_name}: #{e.message}"
|
225
|
+
return false
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
# 从缓存恢复pod
|
230
|
+
def self.restore_from_cache(pod_name, version, target_dir)
|
231
|
+
cache_dir = cache_dir_for_pod(pod_name, version)
|
232
|
+
|
233
|
+
return false unless Dir.exist?(cache_dir)
|
234
|
+
|
235
|
+
begin
|
236
|
+
# 删除目标目录
|
237
|
+
FileUtils.rm_rf(target_dir) if Dir.exist?(target_dir)
|
238
|
+
|
239
|
+
# 从缓存拷贝到目标目录
|
240
|
+
FileUtils.cp_r(cache_dir, target_dir, :remove_destination => true)
|
241
|
+
|
242
|
+
# 删除缓存标记文件
|
243
|
+
hash_file = File.join(target_dir, '.cache_hash')
|
244
|
+
File.delete(hash_file) if File.exist?(hash_file)
|
245
|
+
|
246
|
+
Pod::UI.puts "📦 Restored #{pod_name} (#{version}) from cache"
|
247
|
+
return true
|
248
|
+
rescue => e
|
249
|
+
Pod::UI.puts "❌ Failed to restore #{pod_name} from cache: #{e.message}"
|
250
|
+
return false
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
# 保存到缓存(如果缓存不存在的话)
|
255
|
+
def self.save_to_cache(pod_name, version, source_dir)
|
256
|
+
return unless Dir.exist?(source_dir)
|
257
|
+
|
258
|
+
# 检查缓存是否已经存在,如果存在则不覆盖
|
259
|
+
if cache_exists?(pod_name, version)
|
260
|
+
Pod::UI.puts "📦 Cache already exists for #{pod_name} (#{version}), skipping save"
|
261
|
+
return true
|
262
|
+
end
|
263
|
+
|
264
|
+
cache_dir = cache_dir_for_pod(pod_name, version)
|
265
|
+
|
266
|
+
begin
|
267
|
+
# 创建缓存目录
|
268
|
+
FileUtils.mkdir_p(File.dirname(cache_dir))
|
269
|
+
|
270
|
+
# 删除旧缓存(如果存在)
|
271
|
+
FileUtils.rm_rf(cache_dir) if Dir.exist?(cache_dir)
|
272
|
+
|
273
|
+
# 拷贝到缓存
|
274
|
+
FileUtils.cp_r(source_dir, cache_dir, :remove_destination => true)
|
275
|
+
|
276
|
+
# 保存哈希值
|
277
|
+
source_hash = calculate_dir_hash(source_dir)
|
278
|
+
hash_file = File.join(cache_dir, '.cache_hash')
|
279
|
+
File.write(hash_file, source_hash)
|
280
|
+
|
281
|
+
Pod::UI.puts "💾 Saved #{pod_name} (#{version}) to cache"
|
282
|
+
return true
|
283
|
+
rescue => e
|
284
|
+
Pod::UI.puts "❌ Failed to save #{pod_name} to cache: #{e.message}"
|
285
|
+
return false
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
# 尝试从缓存拷贝到目标目录(如果缓存存在)
|
290
|
+
def self.copy_from_cache_if_exists(pod_name, version, target_dir)
|
291
|
+
return false unless cache_exists?(pod_name, version)
|
292
|
+
|
293
|
+
cache_dir = cache_dir_for_pod(pod_name, version)
|
294
|
+
|
295
|
+
begin
|
296
|
+
# 删除目标目录
|
297
|
+
FileUtils.rm_rf(target_dir) if Dir.exist?(target_dir)
|
298
|
+
|
299
|
+
# 从缓存拷贝到目标目录
|
300
|
+
FileUtils.cp_r(cache_dir, target_dir, :remove_destination => true)
|
301
|
+
|
302
|
+
# 删除缓存标记文件
|
303
|
+
hash_file = File.join(target_dir, '.cache_hash')
|
304
|
+
File.delete(hash_file) if File.exist?(hash_file)
|
305
|
+
|
306
|
+
Pod::UI.puts "📦 Copied #{pod_name} (#{version}) from cache"
|
307
|
+
return true
|
308
|
+
rescue => e
|
309
|
+
Pod::UI.puts "❌ Failed to copy #{pod_name} from cache: #{e.message}"
|
310
|
+
return false
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
# 清理过期缓存(可选功能)
|
315
|
+
def self.clean_expired_cache(days = 30)
|
316
|
+
return unless Dir.exist?(cache_root_dir)
|
317
|
+
|
318
|
+
cutoff_time = Time.now - (days * 24 * 60 * 60)
|
319
|
+
cleaned_count = 0
|
320
|
+
|
321
|
+
Dir.glob("#{cache_root_dir}/*/*").each do |cache_dir|
|
322
|
+
next unless Dir.exist?(cache_dir)
|
323
|
+
|
324
|
+
if File.mtime(cache_dir) < cutoff_time
|
325
|
+
begin
|
326
|
+
FileUtils.rm_rf(cache_dir)
|
327
|
+
cleaned_count += 1
|
328
|
+
Pod::UI.puts "🗑️ Cleaned expired cache: #{File.basename(File.dirname(cache_dir))}/#{File.basename(cache_dir)}"
|
329
|
+
rescue => e
|
330
|
+
Pod::UI.puts "❌ Failed to clean cache #{cache_dir}: #{e.message}"
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
Pod::UI.puts "✅ Cleaned #{cleaned_count} expired cache entries" if cleaned_count > 0
|
336
|
+
end
|
337
|
+
|
338
|
+
# 获取缓存统计信息
|
339
|
+
def self.cache_stats
|
340
|
+
return {} unless Dir.exist?(cache_root_dir)
|
341
|
+
|
342
|
+
stats = {
|
343
|
+
total_pods: 0,
|
344
|
+
total_versions: 0,
|
345
|
+
total_size: 0
|
346
|
+
}
|
347
|
+
|
348
|
+
Dir.glob("#{cache_root_dir}/*").each do |pod_dir|
|
349
|
+
next unless Dir.exist?(pod_dir)
|
350
|
+
|
351
|
+
stats[:total_pods] += 1
|
352
|
+
|
353
|
+
Dir.glob("#{pod_dir}/*").each do |version_dir|
|
354
|
+
next unless Dir.exist?(version_dir)
|
355
|
+
|
356
|
+
stats[:total_versions] += 1
|
357
|
+
|
358
|
+
# 计算目录大小
|
359
|
+
size = `du -sk "#{version_dir}" 2>/dev/null`.split.first.to_i * 1024
|
360
|
+
stats[:total_size] += size
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
stats
|
365
|
+
end
|
366
|
+
|
367
|
+
# 打印缓存统计信息
|
368
|
+
def self.print_cache_stats
|
369
|
+
stats = cache_stats
|
370
|
+
if stats[:total_pods] > 0
|
371
|
+
size_mb = (stats[:total_size] / 1024.0 / 1024.0).round(2)
|
372
|
+
Pod::UI.puts "📊 Cache Stats: #{stats[:total_pods]} pods, #{stats[:total_versions]} versions, #{size_mb} MB"
|
373
|
+
else
|
374
|
+
Pod::UI.puts "📊 Cache is empty"
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
# 从 ~/.PodCache 恢复缓存到 _prebuild 目录
|
379
|
+
def self.restore_from_pod_cache(pod_name, prebuild_sandbox)
|
380
|
+
begin
|
381
|
+
Pod::UI.puts "🔍 Checking cache for #{pod_name} #{prebuild_sandbox}"
|
382
|
+
|
383
|
+
restored_count = 0
|
384
|
+
missing_pods = []
|
385
|
+
|
386
|
+
# 获取pod版本信息
|
387
|
+
pod_version = get_pod_version(prebuild_sandbox, pod_name)
|
388
|
+
|
389
|
+
# 检查缓存是否存在
|
390
|
+
if cache_exists?(pod_name, pod_version)
|
391
|
+
# 从缓存复制到_prebuild目录
|
392
|
+
cache_dir = cache_dir_for_pod(pod_name, pod_version)
|
393
|
+
target_dir = prebuild_sandbox.framework_folder_path_for_pod_name(pod_name)
|
394
|
+
|
395
|
+
# 确保目标目录存在
|
396
|
+
target_dir.parent.mkpath unless target_dir.parent.exist?
|
397
|
+
|
398
|
+
# 复制缓存到_prebuild
|
399
|
+
FileUtils.cp_r(cache_dir, target_dir, :remove_destination => true)
|
400
|
+
|
401
|
+
# 删除缓存标记文件
|
402
|
+
hash_file = target_dir + '.cache_hash'
|
403
|
+
hash_file.delete if hash_file.exist?
|
404
|
+
|
405
|
+
Pod::UI.puts "📦 Restored #{pod_name} (#{pod_version}) from cache"
|
406
|
+
restored_count += 1
|
407
|
+
else
|
408
|
+
missing_pods << pod_name
|
409
|
+
end
|
410
|
+
|
411
|
+
if missing_pods.empty?
|
412
|
+
Pod::UI.puts "✅ Successfully restored all #{restored_count} pods from cache"
|
413
|
+
return true
|
414
|
+
else
|
415
|
+
Pod::UI.puts "⚠️ Missing cache for #{missing_pods.count} pods: #{missing_pods.join(', ')}"
|
416
|
+
return false
|
417
|
+
end
|
418
|
+
|
419
|
+
rescue => e
|
420
|
+
Pod::UI.puts "❌ Error restoring from cache: #{e.message}"
|
421
|
+
return false
|
422
|
+
end
|
423
|
+
end
|
424
|
+
end
|
425
|
+
end
|
426
|
+
end
|
@@ -0,0 +1,171 @@
|
|
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
|
+
def simulator_build_disable!
|
17
|
+
DSL.simulator_build_enabled = false
|
18
|
+
end
|
19
|
+
|
20
|
+
# Enable bitcode for prebuilt frameworks
|
21
|
+
def enable_bitcode_for_prebuilt_frameworks!
|
22
|
+
DSL.bitcode_enabled = true
|
23
|
+
end
|
24
|
+
|
25
|
+
# Don't remove source code of prebuilt pods
|
26
|
+
# It may speed up the pod install if git didn't
|
27
|
+
# include the `Pods` folder
|
28
|
+
def keep_source_code_for_prebuilt_frameworks!
|
29
|
+
DSL.dont_remove_source_code = true
|
30
|
+
end
|
31
|
+
|
32
|
+
# Add custom xcodebuild option to the prebuilding action
|
33
|
+
#
|
34
|
+
# You may use this for your special demands. For example: the default archs in dSYMs
|
35
|
+
# of prebuilt frameworks is 'arm64 armv7 x86_64', and no 'i386' for 32bit simulator.
|
36
|
+
# It may generate a warning when building for a 32bit simulator. You may add following
|
37
|
+
# to your podfile
|
38
|
+
#
|
39
|
+
# ` set_custom_xcodebuild_options_for_prebuilt_frameworks :simulator => "ARCHS=$(ARCHS_STANDARD)" `
|
40
|
+
#
|
41
|
+
# Another example to disable the generating of dSYM file:
|
42
|
+
#
|
43
|
+
# ` set_custom_xcodebuild_options_for_prebuilt_frameworks "DEBUG_INFORMATION_FORMAT=dwarf"`
|
44
|
+
#
|
45
|
+
#
|
46
|
+
# @param [String or Hash] options
|
47
|
+
#
|
48
|
+
# If is a String, it will apply for device and simulator. Use it just like in the commandline.
|
49
|
+
# If is a Hash, it should be like this: { :device => "XXXXX", :simulator => "XXXXX" }
|
50
|
+
#
|
51
|
+
def set_custom_xcodebuild_options_for_prebuilt_frameworks(options)
|
52
|
+
if options.kind_of? Hash
|
53
|
+
DSL.custom_build_options = [ options[:device] ] unless options[:device].nil?
|
54
|
+
DSL.custom_build_options_simulator = [ options[:simulator] ] unless options[:simulator].nil?
|
55
|
+
elsif options.kind_of? String
|
56
|
+
DSL.custom_build_options = [options]
|
57
|
+
DSL.custom_build_options_simulator = [options]
|
58
|
+
else
|
59
|
+
raise "Wrong type."
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class_attr_accessor :simulator_build_enabled
|
64
|
+
simulator_build_enabled = true
|
65
|
+
|
66
|
+
private
|
67
|
+
class_attr_accessor :prebuild_all
|
68
|
+
prebuild_all = false
|
69
|
+
|
70
|
+
class_attr_accessor :bitcode_enabled
|
71
|
+
bitcode_enabled = false
|
72
|
+
|
73
|
+
class_attr_accessor :dont_remove_source_code
|
74
|
+
dont_remove_source_code = false
|
75
|
+
|
76
|
+
class_attr_accessor :custom_build_options
|
77
|
+
class_attr_accessor :custom_build_options_simulator
|
78
|
+
self.custom_build_options = []
|
79
|
+
self.custom_build_options_simulator = []
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
Pod::HooksManager.register('cocoapods-binary-matchup', :pre_install) do |installer_context|
|
85
|
+
|
86
|
+
require_relative 'helper/feature_switches'
|
87
|
+
if Pod.is_prebuild_stage
|
88
|
+
next
|
89
|
+
end
|
90
|
+
|
91
|
+
# [Check Environment]
|
92
|
+
# check user_framework is on
|
93
|
+
podfile = installer_context.podfile
|
94
|
+
podfile.target_definition_list.each do |target_definition|
|
95
|
+
next if target_definition.prebuild_framework_names.empty?
|
96
|
+
if not target_definition.uses_frameworks?
|
97
|
+
STDERR.puts "[!] Cocoapods-binary requires `use_frameworks!`".red
|
98
|
+
exit
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
# -- step 1: prebuild framework ---
|
104
|
+
# Execute a sperated pod install, to generate targets for building framework,
|
105
|
+
# then compile them to framework files.
|
106
|
+
require_relative 'helper/prebuild_sandbox'
|
107
|
+
require_relative 'Prebuild'
|
108
|
+
|
109
|
+
Pod::UI.puts "🚀 Prebuild frameworks"
|
110
|
+
|
111
|
+
# Fetch original installer (which is running this pre-install hook) options,
|
112
|
+
# then pass them to our installer to perform update if needed
|
113
|
+
# Looks like this is the most appropriate way to figure out that something should be updated
|
114
|
+
|
115
|
+
update = nil
|
116
|
+
repo_update = nil
|
117
|
+
|
118
|
+
include ObjectSpace
|
119
|
+
ObjectSpace.each_object(Pod::Installer) { |installer|
|
120
|
+
update = installer.update
|
121
|
+
repo_update = installer.repo_update
|
122
|
+
}
|
123
|
+
|
124
|
+
# control features
|
125
|
+
Pod.is_prebuild_stage = true
|
126
|
+
Pod::Podfile::DSL.enable_prebuild_patch true # enable sikpping for prebuild targets
|
127
|
+
Pod::Installer.force_disable_integration true # don't integrate targets
|
128
|
+
Pod::Config.force_disable_write_lockfile true # disbale write lock file for perbuild podfile
|
129
|
+
Pod::Installer.disable_install_complete_message true # disable install complete message
|
130
|
+
|
131
|
+
# make another custom sandbox
|
132
|
+
standard_sandbox = installer_context.sandbox
|
133
|
+
prebuild_sandbox = Pod::PrebuildSandbox.from_standard_sandbox(standard_sandbox)
|
134
|
+
|
135
|
+
# get the podfile for prebuild
|
136
|
+
prebuild_podfile = Pod::Podfile.from_ruby(podfile.defined_in_file)
|
137
|
+
|
138
|
+
# install
|
139
|
+
lockfile = installer_context.lockfile
|
140
|
+
binary_installer = Pod::Installer.new(prebuild_sandbox, prebuild_podfile, lockfile)
|
141
|
+
|
142
|
+
# 优化的三级缓存逻辑
|
143
|
+
if binary_installer.have_exact_prebuild_cache? && !update
|
144
|
+
# 1. 优先:_prebuild 目录已有完整缓存
|
145
|
+
Pod::UI.puts "📦 Using exact prebuild cache from _Prebuild"
|
146
|
+
binary_installer.install_when_cache_hit!
|
147
|
+
else
|
148
|
+
# 3. 最后:重新构建
|
149
|
+
Pod::UI.puts "🔨 Building frameworks (no cache available)"
|
150
|
+
binary_installer.update = update
|
151
|
+
binary_installer.repo_update = repo_update
|
152
|
+
binary_installer.install!
|
153
|
+
end
|
154
|
+
|
155
|
+
|
156
|
+
# reset the environment
|
157
|
+
Pod.is_prebuild_stage = false
|
158
|
+
Pod::Installer.force_disable_integration false
|
159
|
+
Pod::Podfile::DSL.enable_prebuild_patch false
|
160
|
+
Pod::Config.force_disable_write_lockfile false
|
161
|
+
Pod::Installer.disable_install_complete_message false
|
162
|
+
|
163
|
+
|
164
|
+
# -- step 2: pod install ---
|
165
|
+
# install
|
166
|
+
Pod::UI.puts "\n"
|
167
|
+
Pod::UI.puts "🤖 Pod Install"
|
168
|
+
require_relative 'Integration'
|
169
|
+
# go on the normal install step ...
|
170
|
+
end
|
171
|
+
|