pod-binary-pack 1.0.0
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 +7 -0
- data/CHANGELOG.md +15 -0
- data/LICENSE.txt +21 -0
- data/README.md +147 -0
- data/exe/pod-binary-pack +6 -0
- data/lib/core/auto_build.rb +299 -0
- data/lib/core/build_item.rb +69 -0
- data/lib/core/cli.rb +442 -0
- data/lib/core/commit.rb +19 -0
- data/lib/core/config.rb +56 -0
- data/lib/core/cost.rb +68 -0
- data/lib/core/git_helper.rb +222 -0
- data/lib/core/github_fork.rb +425 -0
- data/lib/core/github_helper.rb +334 -0
- data/lib/core/hook_manager.rb +32 -0
- data/lib/core/hosting/gitee_client.rb +114 -0
- data/lib/core/hosting/gitee_provider.rb +312 -0
- data/lib/core/hosting/gitlab_provider.rb +342 -0
- data/lib/core/hosting/provider.rb +96 -0
- data/lib/core/hosting.rb +43 -0
- data/lib/core/logger.rb +66 -0
- data/lib/core/podspec.rb +126 -0
- data/lib/core/push.rb +80 -0
- data/lib/core/repo.rb +284 -0
- data/lib/core/repo_cocoapods.rb +64 -0
- data/lib/core/repo_module.rb +281 -0
- data/lib/core/repo_module_binary.rb +439 -0
- data/lib/core/repo_module_source.rb +506 -0
- data/lib/core/repo_xcode.rb +41 -0
- data/lib/core/repo_xcode_package.rb +713 -0
- data/lib/core/repo_xcode_shell.rb +170 -0
- data/lib/core/util/file_helper.rb +100 -0
- data/lib/core/util/string.rb +35 -0
- data/lib/pod/binary_pack/version.rb +7 -0
- data/lib/pod/binary_pack.rb +9 -0
- metadata +144 -0
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require 'json'
|
|
5
|
+
require 'digest'
|
|
6
|
+
require_relative 'repo_module'
|
|
7
|
+
require_relative 'util/file_helper'
|
|
8
|
+
|
|
9
|
+
module Build
|
|
10
|
+
class RepoModuleBinary < RepoModule
|
|
11
|
+
|
|
12
|
+
attr_accessor :framework_binary_path
|
|
13
|
+
|
|
14
|
+
# 合并二进制
|
|
15
|
+
def copy_framework(repo_xcode_package, repo_module_source)
|
|
16
|
+
build_real_device = build_item.real_device
|
|
17
|
+
build_simulator = build_item.simulator
|
|
18
|
+
if not build_real_device and not build_simulator
|
|
19
|
+
return
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
@framework_name = repo_module_source.framework_name
|
|
23
|
+
|
|
24
|
+
origin_framework_real_device = "#{repo_xcode_package.real_device_archive_path}/Products/Library/Frameworks/#{framework_name}.framework"
|
|
25
|
+
origin_framework_simulator = "#{repo_xcode_package.simulator_archive_path}/Products/Library/Frameworks/#{framework_name}.framework"
|
|
26
|
+
|
|
27
|
+
if build_real_device and not File.exist?(origin_framework_real_device)
|
|
28
|
+
error = "真机架构framework不存在"
|
|
29
|
+
pusher.push("❌ 打包失败", error)
|
|
30
|
+
Process.exit(-1)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
if build_simulator and not File.exist?(origin_framework_simulator)
|
|
34
|
+
error = "模拟器架构framework不存在"
|
|
35
|
+
pusher.push("❌ 打包失败", error)
|
|
36
|
+
Process.exit(-1)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
@framework_binary_path = "#{self.path}/#{framework_name}.xcframework"
|
|
40
|
+
|
|
41
|
+
command = "xcodebuild -create-xcframework"
|
|
42
|
+
|
|
43
|
+
if build_real_device
|
|
44
|
+
command += " -framework \"#{origin_framework_real_device}\""
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
if build_simulator
|
|
48
|
+
command += " -framework \"#{origin_framework_simulator}\""
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
command += " -output \"#{framework_binary_path}\""
|
|
52
|
+
|
|
53
|
+
Build.log.step("合并并拷贝framework(#{File.basename(self.path)})")
|
|
54
|
+
Build.log.debug(command)
|
|
55
|
+
|
|
56
|
+
res = system command
|
|
57
|
+
|
|
58
|
+
unless res == true
|
|
59
|
+
error = "合并framework失败"
|
|
60
|
+
pusher.push("❌ 打包失败", error)
|
|
61
|
+
Process.exit(-1)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def copy_resources(repo_xcode_package, repo_module_source)
|
|
66
|
+
Build.log.step("拷贝资源文件到framework中(#{File.basename(self.path)})")
|
|
67
|
+
bundle_path_array = repo_xcode_package.resource_bundles_paths
|
|
68
|
+
resource_paths = repo_xcode_package.resource_paths
|
|
69
|
+
|
|
70
|
+
if bundle_path_array.length == 0
|
|
71
|
+
Build.log.detail("源码podspec里未指定resource_bundles")
|
|
72
|
+
end
|
|
73
|
+
if resource_paths.length == 0
|
|
74
|
+
Build.log.detail("源码podspec里未指定resource")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
result_array = bundle_path_array + resource_paths
|
|
78
|
+
result_array.each { |p| Build.log.debug("资源文件: #{p}") }
|
|
79
|
+
if result_array.length == 0
|
|
80
|
+
Build.log.detail("无资源文件,不拷贝")
|
|
81
|
+
return
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# 获取framework路径
|
|
85
|
+
framework_path_array = Array.new
|
|
86
|
+
framework = "#{repo_module_source.framework_name}.framework"
|
|
87
|
+
FileHelper.recursion_find(self.path, framework, framework_path_array)
|
|
88
|
+
framework_path_array.each { |p| Build.log.debug("目标framework: #{p}") }
|
|
89
|
+
|
|
90
|
+
# 拷贝到目标路径
|
|
91
|
+
framework_path_array.each { |framework_path|
|
|
92
|
+
result_array.each { |bundle_path|
|
|
93
|
+
bundle_name = File.basename(bundle_path)
|
|
94
|
+
des_bundle_path = "#{framework_path}/#{bundle_name}"
|
|
95
|
+
# 如果不存在 则拷贝
|
|
96
|
+
unless File.exist?(des_bundle_path)
|
|
97
|
+
FileUtils.cp_r(bundle_path, framework_path)
|
|
98
|
+
end
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
Build.log.detail("资源文件拷贝完成")
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def copy_podspec(repo_xcode_package, repo_module_source)
|
|
105
|
+
|
|
106
|
+
build_real_device = build_item.real_device
|
|
107
|
+
build_simulator = build_item.simulator
|
|
108
|
+
if not build_real_device and not build_simulator
|
|
109
|
+
return
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
Build.log.step("拷贝podspec文件到framework中(#{File.basename(self.path)})")
|
|
113
|
+
@podspec_json_path = "#{self.path}/#{self.pod_name}.podspec.json"
|
|
114
|
+
|
|
115
|
+
# xcframework 内 slice 目录名不固定(不同Xcode/构建参数产物不同),必须动态解析:
|
|
116
|
+
# 真机: 通常 ios-arm64
|
|
117
|
+
# 模拟器: 旧Intel-only为 ios-x86_64-simulator;Apple Silicon fat为 ios-arm64_x86_64-simulator;仅arm64为 ios-arm64-simulator
|
|
118
|
+
# 本方法返回的是 podspec resources glob 用的 slice 目录名(真机/模拟器二选一)
|
|
119
|
+
source_files_dir = xcframework_slice_dir(device: build_real_device, simulator: build_simulator)
|
|
120
|
+
unless source_files_dir
|
|
121
|
+
error = "无法在#{framework_binary_path}中解析到#{build_real_device ? '真机' : '模拟器'}slice目录"
|
|
122
|
+
pusher.push("❌ 打包失败", error)
|
|
123
|
+
Process.exit(-1)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# 原始podspec
|
|
127
|
+
@podspec_content = repo_module_source.podspec_content
|
|
128
|
+
|
|
129
|
+
podspec_content.delete("license")
|
|
130
|
+
podspec_content.delete("module_map")
|
|
131
|
+
podspec_content.delete("public_header_files")
|
|
132
|
+
podspec_content.delete("private_header_files")
|
|
133
|
+
podspec_content.delete("exclude_files")
|
|
134
|
+
podspec_content.delete("prefix_header_file")
|
|
135
|
+
# 二进制包仅含 xcframework,没有源码,prepare_command 既无意义也无处执行;
|
|
136
|
+
# 保留会导致安装期(如 SQLCipher 的 ./configure)在二进制仓 checkout 目录
|
|
137
|
+
# 因脚本不存在直接报错,因此所有组件的二进制 spec 统一移除
|
|
138
|
+
podspec_content.delete("prepare_command")
|
|
139
|
+
|
|
140
|
+
podspec_content["source"] = {} #二进制后, 只从tag读取,去除其他的标志信息
|
|
141
|
+
podspec_content["source"]["git"] = self.ssh_url
|
|
142
|
+
podspec_content["source"]["tag"] = self.pod_version_final
|
|
143
|
+
|
|
144
|
+
podspec_content["source_files"] = "**/*/#{source_files_dir}/*.framework/Headers/*.h"
|
|
145
|
+
podspec_content["version"] = self.pod_version_final
|
|
146
|
+
podspec_content["vendored_frameworks"] = "*.xcframework"
|
|
147
|
+
|
|
148
|
+
# 资源统一汇总root及所有subspec(多subspec整包构建后资源都在同一个framework内)
|
|
149
|
+
podspec_content["resources"] = []
|
|
150
|
+
|
|
151
|
+
# 1) 松散resources/resource glob:按glob在源码工作副本中找到真实文件后映射进framework
|
|
152
|
+
repo_module_source.all_resource_globs.each do |resource|
|
|
153
|
+
# 子目录podspec时先按podspec所在目录匹配,再回退仓库根目录
|
|
154
|
+
search_roots = [repo_module_source.component_root_dir, repo_module_source.path].uniq
|
|
155
|
+
real_paths = search_roots.flat_map { |root| Dir["#{root}/#{resource}"] }.uniq
|
|
156
|
+
real_paths.each do |real_path|
|
|
157
|
+
resource_name = File.basename(real_path)
|
|
158
|
+
podspec_content["resources"] << "**/*/#{source_files_dir}/*.framework/#{resource_name}"
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
podspec_content.delete("resource")
|
|
162
|
+
|
|
163
|
+
# 2) resource_bundles:root及各subspec声明的bundle名(整包构建后均在framework内)
|
|
164
|
+
repo_module_source.all_resource_bundle_names.each do |bundle_name|
|
|
165
|
+
podspec_content["resources"] << "**/*/#{source_files_dir}/*.framework/#{bundle_name}.bundle"
|
|
166
|
+
end
|
|
167
|
+
podspec_content.delete("resource_bundles")
|
|
168
|
+
podspec_content.delete("resource_bundle")
|
|
169
|
+
podspec_content["resources"].uniq!
|
|
170
|
+
|
|
171
|
+
# 资源文件-podspec
|
|
172
|
+
podspec_content["resources"] << "**/*/#{source_files_dir}/*.framework/*.podspec.json"
|
|
173
|
+
|
|
174
|
+
# 二进制扩展信息:源码podspec已带build_metadata基础字段,这里补充产物相关信息
|
|
175
|
+
metadata = podspec_content[PODSPEC_METADATA_KEY] || {}
|
|
176
|
+
# 代码地址改为二进制仓库地址
|
|
177
|
+
metadata["address"] = self.http_url
|
|
178
|
+
metadata["archive"] = repo_xcode_package.debug_mode ? "Debug" : "Release"
|
|
179
|
+
# 组件在整体App包中的实际占用体积(只统计真机slice的framework,排除dSYM/模拟器slice/podspec)
|
|
180
|
+
metadata["framework_size"] = framework_size_format
|
|
181
|
+
# 是否SNAPSHOT包
|
|
182
|
+
metadata["snapshot"] = build_item.snapshot
|
|
183
|
+
# xcframework实际包含的CPU架构,按真机/模拟器分类
|
|
184
|
+
metadata["architectures"] = xcframework_architectures
|
|
185
|
+
# 构建所用Xcode版本,如 16.4 (16F6)
|
|
186
|
+
metadata["xcode_version"] = xcode_version
|
|
187
|
+
# xcframework产物中是否包含dSYM调试符号
|
|
188
|
+
metadata["include_dsym"] = include_dsym?
|
|
189
|
+
# xcframework内Mach-O二进制的SHA256,用于产物完整性校验/比对
|
|
190
|
+
metadata["framework_checksum"] = xcframework_checksum
|
|
191
|
+
podspec_content[PODSPEC_METADATA_KEY] = metadata
|
|
192
|
+
|
|
193
|
+
# swift
|
|
194
|
+
if podspec_content.has_key?("swift_version")
|
|
195
|
+
podspec_content["pod_target_xcconfig"] = {"BUILD_LIBRARY_FOR_DISTRIBUTION": true}
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# 处理子仓库
|
|
199
|
+
subspecs = podspec_content["subspecs"]
|
|
200
|
+
if subspecs.respond_to?(:each)
|
|
201
|
+
subspecs.each do |subspec|
|
|
202
|
+
subspec.delete("resources")
|
|
203
|
+
subspec.delete("resource_bundles")
|
|
204
|
+
subspec.delete("exclude_files")
|
|
205
|
+
subspec.delete("public_header_files")
|
|
206
|
+
subspec.delete("project_header_files")
|
|
207
|
+
if subspec["ios"] and subspec["ios"].class == Hash
|
|
208
|
+
sun = subspec["ios"]
|
|
209
|
+
sun.delete("resources")
|
|
210
|
+
sun.delete("resource_bundles")
|
|
211
|
+
sun.delete("exclude_files")
|
|
212
|
+
sun.delete("public_header_files")
|
|
213
|
+
sun.delete("project_header_files")
|
|
214
|
+
sun.delete("source_files")
|
|
215
|
+
end
|
|
216
|
+
subspec["source_files"] = "placeholder.m"
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
## 重写文件
|
|
221
|
+
new_file = File.new(podspec_json_path, "w+")
|
|
222
|
+
if new_file
|
|
223
|
+
new_file.syswrite(JSON.pretty_generate(podspec_content))
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# 拷贝podspec文件到实际存在的真机/模拟器slice(目录名动态解析)
|
|
227
|
+
[xcframework_slice_dir(device: true), xcframework_slice_dir(simulator: true)].compact.uniq.each do |slice_dir|
|
|
228
|
+
framework_slice_path = File.join(framework_binary_path, slice_dir, "#{framework_name}.framework")
|
|
229
|
+
FileUtils.cp(podspec_json_path, framework_slice_path) if File.directory?(framework_slice_path)
|
|
230
|
+
end
|
|
231
|
+
Build.log.detail("framework 产物拷贝完成")
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# 从xcframework的Info.plist解析真机/模拟器slice的目录名(LibraryIdentifier)。
|
|
235
|
+
# 兼容三种命名: ios-arm64(真机) / ios-x86_64-simulator(旧Intel) /
|
|
236
|
+
# ios-arm64_x86_64-simulator(fat) / ios-arm64-simulator(仅arm64)
|
|
237
|
+
# @return [String, nil] slice目录名
|
|
238
|
+
def xcframework_slice_dir(device: false, simulator: false)
|
|
239
|
+
@xcframework_slices ||= parse_xcframework_slices
|
|
240
|
+
if simulator
|
|
241
|
+
@xcframework_slices[:simulator]
|
|
242
|
+
elsif device
|
|
243
|
+
@xcframework_slices[:device]
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def parse_xcframework_slices
|
|
248
|
+
slices = { device: nil, simulator: nil }
|
|
249
|
+
plist_path = File.join(framework_binary_path, 'Info.plist')
|
|
250
|
+
json_text = `plutil -convert json -o - "#{plist_path}" 2>/dev/null`
|
|
251
|
+
libraries = JSON.parse(json_text)["AvailableLibraries"] || []
|
|
252
|
+
libraries.each do |library|
|
|
253
|
+
identifier = library["LibraryIdentifier"].to_s
|
|
254
|
+
if library["SupportedPlatformVariant"] == "simulator"
|
|
255
|
+
slices[:simulator] ||= identifier
|
|
256
|
+
elsif library["SupportedPlatform"] == "ios"
|
|
257
|
+
slices[:device] ||= identifier
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
slices
|
|
261
|
+
rescue => e
|
|
262
|
+
Build.log.warn("解析xcframework slice目录失败(#{framework_binary_path}):#{e.message}")
|
|
263
|
+
slices
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# 解析xcframework实际包含的CPU架构,按真机/模拟器分类汇总
|
|
267
|
+
# @return [Hash] 如 {"device"=>["arm64"], "simulator"=>["arm64"]}
|
|
268
|
+
def xcframework_architectures
|
|
269
|
+
result = {"device" => [], "simulator" => []}
|
|
270
|
+
plist_path = "#{framework_binary_path}/Info.plist"
|
|
271
|
+
|
|
272
|
+
begin
|
|
273
|
+
# 使用系统自带 plutil 将 Info.plist 转成 JSON 读取 AvailableLibraries
|
|
274
|
+
json_text = `plutil -convert json -o - "#{plist_path}" 2>/dev/null`
|
|
275
|
+
libraries = JSON.parse(json_text)["AvailableLibraries"] || []
|
|
276
|
+
libraries.each do |library|
|
|
277
|
+
# xcframework中真机/模拟器的 SupportedPlatform 均为 "ios",
|
|
278
|
+
# 模拟器slice通过 SupportedPlatformVariant == "simulator" 区分
|
|
279
|
+
if library["SupportedPlatformVariant"] == "simulator"
|
|
280
|
+
platform = "simulator"
|
|
281
|
+
elsif library["SupportedPlatform"] == "ios"
|
|
282
|
+
platform = "device"
|
|
283
|
+
else
|
|
284
|
+
next
|
|
285
|
+
end
|
|
286
|
+
(library["SupportedArchitectures"] || []).each { |arch| result[platform] << arch }
|
|
287
|
+
end
|
|
288
|
+
rescue => exception
|
|
289
|
+
Build.log.warn("解析xcframework架构信息失败:#{exception.message},将按本次构建开关兜底为arm64")
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
# 兜底:plist解析失败时按本次构建开关记录arm64
|
|
293
|
+
if result["device"].empty? && result["simulator"].empty?
|
|
294
|
+
result["device"] = ["arm64"] if build_item.real_device
|
|
295
|
+
result["simulator"] = ["arm64"] if build_item.simulator
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
result.transform_values { |archs| archs.uniq.sort }
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
# 组件在整体App包中的实际占用体积:
|
|
302
|
+
# - framework组件:只统计真机slice的Foo.framework(Mach-O二进制+资源bundle+Info.plist),
|
|
303
|
+
# 排除dSYM、模拟器slice、podspec.json等不会进入App包的内容;仅模拟器构建时退化为统计模拟器slice。
|
|
304
|
+
# 注意这是"解压后占用"口径,非App Store压缩后的下载体积
|
|
305
|
+
# - 纯头文件组件:头文件仅参与编译,不进入App包,体积贡献为0
|
|
306
|
+
# - 解析不到framework目录时视为产物结构异常,直接打包失败
|
|
307
|
+
# @return [String] 体积描述(KB/MB格式)
|
|
308
|
+
def framework_size_format
|
|
309
|
+
return "0KB" if build_item.header_only
|
|
310
|
+
|
|
311
|
+
# 真机包才是发布给用户的产物;仅模拟器构建时用模拟器slice兜底
|
|
312
|
+
slice = xcframework_slice_dir(device: true) || xcframework_slice_dir(simulator: true)
|
|
313
|
+
framework_dir = slice ? File.join(framework_binary_path, slice, "#{framework_name}.framework") : nil
|
|
314
|
+
|
|
315
|
+
unless framework_dir && File.directory?(framework_dir)
|
|
316
|
+
error = "无法在#{framework_binary_path}中解析到进App包的framework目录,产物结构异常"
|
|
317
|
+
pusher.push("❌ 打包失败", error)
|
|
318
|
+
Process.exit(-1)
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
total = Dir.glob(File.join(framework_dir, '**', '*'))
|
|
322
|
+
.select { |f| File.file?(f) }
|
|
323
|
+
.sum { |f| File.size(f).to_f / 1024 }
|
|
324
|
+
total > 1024 ? "#{(total * 0.001).round(1)}MB" : "#{total.round(0)}KB"
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
# xcframework产物中是否包含dSYM调试符号
|
|
328
|
+
# @return [Boolean]
|
|
329
|
+
def include_dsym?
|
|
330
|
+
!Dir.glob("#{framework_binary_path}/**/*.dSYM").empty?
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
# 本次构建使用的Xcode版本,如 "16.4 (16F6)",获取失败返回空字符串
|
|
334
|
+
# @return [String]
|
|
335
|
+
def xcode_version
|
|
336
|
+
output = `xcodebuild -version 2>/dev/null`.strip
|
|
337
|
+
version = output[/Xcode\s+(\S+)/, 1]
|
|
338
|
+
build = output[/Build version\s+(\S+)/, 1]
|
|
339
|
+
return "" if version.nil?
|
|
340
|
+
build ? "#{version} (#{build})" : version
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
# 计算xcframework内各slice的Mach-O可执行文件的合并SHA256。
|
|
344
|
+
# 只摘要真实二进制内容(按相对路径排序),不受Info.plist时间戳/文件修改时间影响,
|
|
345
|
+
# 可用于产物完整性校验以及判断两次构建的二进制是否实质一致。
|
|
346
|
+
# @return [String] 64位SHA256十六进制串,无可用二进制时返回空字符串
|
|
347
|
+
def xcframework_checksum
|
|
348
|
+
# 各slice的可执行文件路径形如: ios-arm64/Foo.framework/Foo
|
|
349
|
+
binary_paths = Dir.glob("#{framework_binary_path}/**/*.framework/*").select do |file_path|
|
|
350
|
+
File.file?(file_path) && File.basename(file_path) == framework_name
|
|
351
|
+
end.sort
|
|
352
|
+
|
|
353
|
+
return "" if binary_paths.empty?
|
|
354
|
+
|
|
355
|
+
digest = Digest::SHA256.new
|
|
356
|
+
binary_paths.each do |binary_path|
|
|
357
|
+
relative_path = Pathname.new(binary_path).relative_path_from(Pathname.new(framework_binary_path)).to_s
|
|
358
|
+
digest.update(relative_path)
|
|
359
|
+
digest.update(File.binread(binary_path))
|
|
360
|
+
end
|
|
361
|
+
digest.hexdigest
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
# 纯头文件组件的二进制发布:二进制仓直接放头文件(保持目录结构) + 普通形态podspec.json。
|
|
365
|
+
# header-only无需编译,二进制形态与源码等价,不做任何framework转换
|
|
366
|
+
def publish_header_only(repo_module_source)
|
|
367
|
+
return unless build_item.header_only
|
|
368
|
+
|
|
369
|
+
Build.log.step("纯头文件组件发布(#{File.basename(self.path)})")
|
|
370
|
+
|
|
371
|
+
copy_header_only_files(repo_module_source)
|
|
372
|
+
write_header_only_podspec(repo_module_source)
|
|
373
|
+
|
|
374
|
+
Build.log.detail("纯头文件组件头文件拷贝完成")
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
private
|
|
378
|
+
|
|
379
|
+
# 按podspec的source_files(root+subspecs)把头文件从源码工作副本按相对目录拷到二进制仓
|
|
380
|
+
def copy_header_only_files(repo_module_source)
|
|
381
|
+
podspec_content = repo_module_source.podspec_content
|
|
382
|
+
globs = Array(podspec_content["source_files"])
|
|
383
|
+
Array(podspec_content["subspecs"]).each do |subspec|
|
|
384
|
+
globs.concat(Array(subspec["source_files"]))
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
source_root = repo_module_source.component_root_dir
|
|
388
|
+
copied = 0
|
|
389
|
+
globs.flatten.compact.uniq.each do |pattern|
|
|
390
|
+
# 与判定逻辑一致:source_files 指向目录时递归取其中文件,glob/单文件照常展开
|
|
391
|
+
FileHelper.source_files_under(File.join(source_root, pattern)).each do |src|
|
|
392
|
+
relative = Pathname.new(src).relative_path_from(Pathname.new(source_root)).to_s
|
|
393
|
+
dest = File.join(self.path, relative)
|
|
394
|
+
FileUtils.mkdir_p(File.dirname(dest))
|
|
395
|
+
FileUtils.cp(src, dest)
|
|
396
|
+
copied += 1
|
|
397
|
+
end
|
|
398
|
+
end
|
|
399
|
+
Build.log.detail("纯头文件组件共拷贝#{copied}个文件到二进制仓")
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
# 写普通形态podspec.json:source指向二进制仓tag,保留source_files等原字段
|
|
403
|
+
def write_header_only_podspec(repo_module_source)
|
|
404
|
+
# 深拷贝,避免污染源码侧的podspec_content
|
|
405
|
+
content = JSON.parse(JSON.generate(repo_module_source.podspec_content))
|
|
406
|
+
|
|
407
|
+
content.delete("license")
|
|
408
|
+
content["source"] = { "git" => self.ssh_url, "tag" => self.pod_version_final }
|
|
409
|
+
content["version"] = self.pod_version_final
|
|
410
|
+
# 纯头组件不涉及framework,删除仅编译产物相关字段,保留 source_files/public_header_files 等
|
|
411
|
+
content.delete("vendored_frameworks")
|
|
412
|
+
|
|
413
|
+
metadata = content[PODSPEC_METADATA_KEY] || {}
|
|
414
|
+
metadata["address"] = self.http_url
|
|
415
|
+
metadata["archive"] = "HeadersOnly"
|
|
416
|
+
metadata["snapshot"] = build_item.snapshot
|
|
417
|
+
# 头文件仅参与编译,不进入App包,组件在整体包大小中的实际占用为0
|
|
418
|
+
metadata["framework_size"] = "0KB"
|
|
419
|
+
metadata["architectures"] = { "device" => [], "simulator" => [] }
|
|
420
|
+
metadata["include_dsym"] = false
|
|
421
|
+
metadata["framework_checksum"] = ""
|
|
422
|
+
content[PODSPEC_METADATA_KEY] = metadata
|
|
423
|
+
|
|
424
|
+
@podspec_json_path = File.join(self.path, "#{self.pod_name}.podspec.json")
|
|
425
|
+
File.write(@podspec_json_path, JSON.pretty_generate(content))
|
|
426
|
+
# 供二进制仓 git_commit 读取 build_metadata 生成提交信息
|
|
427
|
+
@podspec_content = content
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
public
|
|
431
|
+
|
|
432
|
+
def git_tag
|
|
433
|
+
if build_item.real_device or build_item.simulator or build_item.header_only
|
|
434
|
+
super
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
end
|
|
439
|
+
end
|