cocoapods-bb-PodAssistant 0.3.15.1 → 0.3.15.2
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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0356b0d9093281386d9cf59a6764965aced7e97d319e64b7efc7f087291f6140
|
|
4
|
+
data.tar.gz: 5b2af0ffd94d23af9654d1de3174ede20b7e53f97d00f3f95b7ca90f4410b571
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 94e339e85bc22fbe2a0070ceef07848d897745c4e06b0f3fc66120946273ad229ca0d80962345d03b93370a106ea5079195db8a3b7d9d8a3623a35d83086686b
|
|
7
|
+
data.tar.gz: 5a42db8d7e28f3fc70ca02f950d6b2830a8a56f256b4953dd1cd7410f1b6d0cba74dcdd4b4c285269a502ec7a75a88938965067f246d1e168ccc2283afcbe576
|
|
@@ -6,9 +6,9 @@ require "timeout"
|
|
|
6
6
|
module BBItools
|
|
7
7
|
class DocCGenerator
|
|
8
8
|
PODSPEC_REPO = "babybus-babybus-ios-specs-babybus-specs"
|
|
9
|
+
PODSPEC_REPO_URL = "https://git.babybus.co/babybus/ios/Specs/babybus-specs.git"
|
|
9
10
|
DESTINATION = "generic/platform=iOS Simulator"
|
|
10
11
|
CONFIGURATION = "Debug"
|
|
11
|
-
KEYCHAIN_HOST = "git.babybus.co"
|
|
12
12
|
|
|
13
13
|
def self.generate(options)
|
|
14
14
|
new(options).run
|
|
@@ -26,7 +26,6 @@ module BBItools
|
|
|
26
26
|
@output = options[:output]
|
|
27
27
|
@logs = options[:logs]
|
|
28
28
|
@derived_data = options[:derived_data]
|
|
29
|
-
@lint_root = options[:lint_root]
|
|
30
29
|
@hosting_base_path = options[:hosting_base_path]
|
|
31
30
|
@subspec = options[:subspec]
|
|
32
31
|
end
|
|
@@ -133,9 +132,9 @@ module BBItools
|
|
|
133
132
|
derived_data_root = File.join(local_work_root, "DerivedData")
|
|
134
133
|
FileUtils.mkdir_p(derived_data_root)
|
|
135
134
|
|
|
136
|
-
|
|
137
|
-
FileUtils.
|
|
138
|
-
|
|
135
|
+
pod_install_root = File.join(local_work_root, "PodInstall")
|
|
136
|
+
FileUtils.rm_rf(pod_install_root)
|
|
137
|
+
FileUtils.mkdir_p(pod_install_root)
|
|
139
138
|
|
|
140
139
|
# 最终生成的站点先放在本地
|
|
141
140
|
local_site_output = File.join(local_work_root, "site_output")
|
|
@@ -207,80 +206,86 @@ module BBItools
|
|
|
207
206
|
require_cmd!("git")
|
|
208
207
|
|
|
209
208
|
# ---------------------------------------------------------
|
|
210
|
-
# 4.
|
|
209
|
+
# 4. 创建临时 Podfile + 最小 Xcode 工程,然后执行 pod install
|
|
211
210
|
# ---------------------------------------------------------
|
|
212
|
-
|
|
213
|
-
lint_log_rel = rel_path(lint_log, base_dir: base_dir)
|
|
214
|
-
lint_cmd = [
|
|
215
|
-
"pod", "lib", "lint", podspec_basename,
|
|
216
|
-
"--verbose", "--no-clean", "--skip-tests", "--skip-import-validation",
|
|
217
|
-
"--use-modular-headers", "--use-static-frameworks",
|
|
218
|
-
"--allow-warnings",
|
|
219
|
-
"--sources=#{PODSPEC_REPO}"
|
|
220
|
-
]
|
|
221
|
-
|
|
222
|
-
# 如果指定了 subspec,添加到 lint 命令
|
|
223
|
-
lint_cmd << "--subspec=#{@subspec}" if @subspec
|
|
211
|
+
write_minimal_xcodeproj(File.join(pod_install_root, "App.xcodeproj"), deployment_target)
|
|
224
212
|
|
|
225
|
-
|
|
213
|
+
pod_dep = @subspec ? "#{scheme}/#{@subspec}" : scheme
|
|
226
214
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
"TMPDIR" => lint_root
|
|
231
|
-
}
|
|
215
|
+
# Symbol graph 输出目录(swiftc -emit-symbol-graph 直接写这里,不经过 clang -extract-api)
|
|
216
|
+
symbol_graph_out = File.join(pod_install_root, "symbol-graphs")
|
|
217
|
+
FileUtils.mkdir_p(symbol_graph_out)
|
|
232
218
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
219
|
+
pod_install_podfile = <<~PODFILE
|
|
220
|
+
platform :ios, '#{deployment_target}'
|
|
221
|
+
inhibit_all_warnings!
|
|
222
|
+
use_frameworks! :linkage => :static
|
|
236
223
|
|
|
237
|
-
|
|
224
|
+
source '#{PODSPEC_REPO_URL}'
|
|
238
225
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
f.puts "- lint: 成功"
|
|
242
|
-
else
|
|
243
|
-
f.puts "- lint: 失败(exit=#{lint_exit})"
|
|
226
|
+
target 'App' do
|
|
227
|
+
pod '#{pod_dep}', :path => '#{podspec_dir_abs}'
|
|
244
228
|
end
|
|
245
|
-
f.puts
|
|
246
|
-
f.puts "**pod lib lint 日志(尾部)**"
|
|
247
|
-
f.puts "```"
|
|
248
|
-
tail_lines(lint_log, 200).each { |line| f.puts line }
|
|
249
|
-
f.puts "```"
|
|
250
|
-
end
|
|
251
229
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
if safe_read_text(lint_log).include?("Authentication failed for 'https://#{KEYCHAIN_HOST}/")
|
|
265
|
-
puts "[docc] 提示: 检测到 git clone 认证失败(#{KEYCHAIN_HOST})"
|
|
230
|
+
post_install do |installer|
|
|
231
|
+
installer.pods_project.targets.each do |target|
|
|
232
|
+
target.build_configurations.each do |config|
|
|
233
|
+
config.build_settings['CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES'] = 'YES'
|
|
234
|
+
# 所有 target 都禁用 clang -extract-api(docbuild 会强制开它,我们不用 docbuild)
|
|
235
|
+
config.build_settings['BUILD_DOCUMENTATION_DURING_EACH_BUILD'] = 'NO'
|
|
236
|
+
next unless target.name == '#{scheme}'
|
|
237
|
+
# 只对主 Pod 注入 -emit-symbol-graph,让 swiftc 在编译时直接输出 symbol graph
|
|
238
|
+
# 比 BUILD_DOCUMENTATION_DURING_EACH_BUILD=YES 更精准,不触发 clang -extract-api
|
|
239
|
+
config.build_settings['OTHER_SWIFT_FLAGS'] = '$(inherited) -emit-symbol-graph -emit-symbol-graph-dir #{symbol_graph_out}'
|
|
240
|
+
end
|
|
241
|
+
end
|
|
266
242
|
end
|
|
267
|
-
|
|
268
|
-
|
|
243
|
+
PODFILE
|
|
244
|
+
|
|
245
|
+
File.write(File.join(pod_install_root, "Podfile"), pod_install_podfile)
|
|
246
|
+
|
|
247
|
+
pod_install_log = File.join(logs_dir, "pod-install.log")
|
|
248
|
+
pod_install_cmd = ["pod", "install", "--no-repo-update"]
|
|
249
|
+
|
|
250
|
+
File.open(status_md, "a") { |f| f.puts "- pod_install: #{pod_install_cmd.join(" ")}" }
|
|
251
|
+
|
|
252
|
+
log "执行 pod install: #{pod_dep} (cwd=#{rel_path(pod_install_root, base_dir: base_dir)})"
|
|
253
|
+
pod_install_env = { "HOME" => real_home }
|
|
254
|
+
pod_install_env["GIT_TERMINAL_PROMPT"] = "0" if ENV["BB_DOCC_ALLOW_PROMPT"].to_s.strip != "1"
|
|
255
|
+
|
|
256
|
+
pod_install_exit = run_to_file(pod_install_cmd, cwd: pod_install_root, env: pod_install_env, log_path: pod_install_log)
|
|
257
|
+
|
|
258
|
+
unless pod_install_exit.zero?
|
|
259
|
+
File.open(status_md, "a") do |f|
|
|
260
|
+
f.puts "- pod_install: 失败(exit=#{pod_install_exit})"
|
|
261
|
+
f.puts
|
|
262
|
+
f.puts "**pod install 日志(尾部)**"
|
|
263
|
+
f.puts "```"
|
|
264
|
+
tail_lines(pod_install_log, 200).each { |line| f.puts line }
|
|
265
|
+
f.puts "```"
|
|
266
|
+
end
|
|
267
|
+
puts "[docc] -------- pod install 日志(尾部 200 行)--------"
|
|
268
|
+
puts tail_lines(pod_install_log, 200).join("\n")
|
|
269
|
+
fail!("pod install 失败 (exit=#{pod_install_exit})")
|
|
269
270
|
end
|
|
270
271
|
|
|
272
|
+
File.open(status_md, "a") { |f| f.puts "- pod_install: 成功" }
|
|
273
|
+
|
|
274
|
+
# pod install 后,清理依赖 Pod 中多余的 .docc 目录
|
|
275
|
+
# xcodebuild docbuild 要求每个 target 只能有一个 .docc catalog
|
|
276
|
+
cleaned_docc = remove_extra_docc_catalogs(File.join(pod_install_root, "Pods"))
|
|
277
|
+
File.open(status_md, "a") { |f| f.puts "- cleaned_extra_docc_catalogs: #{cleaned_docc.join(', ')}" } if cleaned_docc.any?
|
|
278
|
+
|
|
271
279
|
# ---------------------------------------------------------
|
|
272
|
-
#
|
|
280
|
+
# 5. 查找 Workspace 并执行 DocBuild
|
|
273
281
|
# ---------------------------------------------------------
|
|
274
|
-
workspace_path_abs =
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
end
|
|
279
|
-
|
|
280
|
-
workspace_path_rel = rel_path(workspace_path_abs.to_s, base_dir: base_dir)
|
|
282
|
+
workspace_path_abs = Dir.glob(File.join(pod_install_root, "*.xcworkspace")).first.to_s
|
|
283
|
+
fail!("未找到 *.xcworkspace(pod_install_root=#{pod_install_root})") if workspace_path_abs.strip.empty?
|
|
284
|
+
|
|
285
|
+
workspace_path_rel = rel_path(workspace_path_abs, base_dir: base_dir)
|
|
281
286
|
fail!("workspace 不存在: #{workspace_path_rel}") unless File.directory?(workspace_path_abs)
|
|
282
287
|
|
|
283
|
-
validation_dir_abs =
|
|
288
|
+
validation_dir_abs = pod_install_root
|
|
284
289
|
File.open(status_md, "a") do |f|
|
|
285
290
|
f.puts "- validation_dir: #{rel_path(validation_dir_abs, base_dir: base_dir)}"
|
|
286
291
|
f.puts "- workspace: #{workspace_path_rel}"
|
|
@@ -314,20 +319,19 @@ module BBItools
|
|
|
314
319
|
FileUtils.mkdir_p(derived_dir)
|
|
315
320
|
derived_dir_rel = rel_path(derived_dir, base_dir: base_dir)
|
|
316
321
|
|
|
317
|
-
|
|
322
|
+
build_log = File.join(logs_dir, "xcodebuild-build.log")
|
|
318
323
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
"
|
|
325
|
-
"-
|
|
326
|
-
"-scheme", scheme, # 通常 Subspec 的 Scheme 还是主 Pod 名
|
|
324
|
+
# 使用 xcodebuild build(非 docbuild)
|
|
325
|
+
# docbuild 会在命令行层面强制对所有 target 开启 ExtractAPI,无视项目里的 BUILD_DOCUMENTATION_DURING_EACH_BUILD=NO
|
|
326
|
+
# build 只对项目里明确设置了 YES 的 target(主 Pod)执行 ExtractAPI,依赖不受影响
|
|
327
|
+
build_cmd = [
|
|
328
|
+
"xcodebuild", "build",
|
|
329
|
+
"-workspace", workspace_path_abs,
|
|
330
|
+
"-scheme", scheme,
|
|
327
331
|
"-destination", DESTINATION,
|
|
328
332
|
"-configuration", CONFIGURATION,
|
|
329
|
-
"-derivedDataPath",
|
|
330
|
-
"ENABLE_USER_SCRIPT_SANDBOXING=NO"
|
|
333
|
+
"-derivedDataPath", derived_dir,
|
|
334
|
+
"ENABLE_USER_SCRIPT_SANDBOXING=NO"
|
|
331
335
|
]
|
|
332
336
|
|
|
333
337
|
docc_catalog_abs = resolve_docc_catalog_abs(podspec_dir_abs, scheme)
|
|
@@ -336,45 +340,55 @@ module BBItools
|
|
|
336
340
|
from_sources = path_inside_dir?(docc_catalog_abs, File.join(podspec_dir_abs, "Sources"))
|
|
337
341
|
File.open(status_md, "a") do |f|
|
|
338
342
|
f.puts "- docc_catalog: #{docc_catalog_rel_for_status}"
|
|
339
|
-
|
|
340
|
-
f.puts "- docc_catalog_source: sources(auto, 未依赖 podspec DOCC_CATALOGS)"
|
|
341
|
-
else
|
|
342
|
-
f.puts "- docc_catalog_source: root(auto)"
|
|
343
|
-
end
|
|
343
|
+
f.puts "- docc_catalog_source: #{from_sources ? 'sources' : 'root'}"
|
|
344
344
|
end
|
|
345
|
-
docbuild_cmd << "DOCC_CATALOGS=#{docc_catalog_abs}"
|
|
346
345
|
else
|
|
347
346
|
File.open(status_md, "a") { |f| f.puts "- docc_catalog: 未找到(将使用工程默认配置)" }
|
|
348
347
|
end
|
|
349
348
|
|
|
350
349
|
if deployment_target && !deployment_target.strip.empty?
|
|
351
|
-
|
|
352
|
-
|
|
350
|
+
build_cmd << "IPHONEOS_DEPLOYMENT_TARGET=#{deployment_target}"
|
|
351
|
+
build_cmd << "SWIFT_VERSION=5.0"
|
|
353
352
|
end
|
|
354
|
-
|
|
353
|
+
build_cmd += ["CODE_SIGNING_ALLOWED=NO", "CODE_SIGNING_REQUIRED=NO", "CODE_SIGN_IDENTITY="]
|
|
354
|
+
|
|
355
|
+
build_cmd_report = build_cmd.map { |arg| arg == workspace_path_abs ? workspace_path_rel : (arg == derived_dir ? derived_dir_rel : arg) }
|
|
356
|
+
File.open(status_md, "a") { |f| f.puts "- build: #{build_cmd_report.join(" ")}" }
|
|
355
357
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
docbuild_cmd_report[docbuild_cmd_report.index(derived_dir_for_docbuild) || 0] = derived_dir_rel
|
|
359
|
-
File.open(status_md, "a") { |f| f.puts "- docbuild: #{docbuild_cmd_report.join(" ")}" }
|
|
358
|
+
log "执行 build (ExtractAPI 仅对 #{scheme}): #{component_name}"
|
|
359
|
+
build_exit = run_to_file(build_cmd, cwd: pod_install_root, env: { "HOME" => real_home }, log_path: build_log)
|
|
360
360
|
|
|
361
|
-
|
|
362
|
-
|
|
361
|
+
# 自动检测 arm64 模拟器不兼容问题(预编译 SDK 无 arm64 simulator slice)
|
|
362
|
+
# 失败后检查日志,有 "unsupported Swift architecture" 时排除 arm64 重试
|
|
363
|
+
if !build_exit.zero? && safe_read_text(build_log).include?("unsupported Swift architecture")
|
|
364
|
+
log "⚠️ 检测到依赖 SDK 不支持 arm64 模拟器,自动切换 x86_64 重试 (EXCLUDED_ARCHS[sdk=iphonesimulator*]=arm64)..."
|
|
365
|
+
File.open(status_md, "a") { |f| f.puts "- build_retry: excluded arm64 for simulator" }
|
|
363
366
|
|
|
364
|
-
|
|
367
|
+
build_log_retry = File.join(logs_dir, "xcodebuild-build-x86_64.log")
|
|
368
|
+
build_cmd_retry = build_cmd + ["EXCLUDED_ARCHS[sdk=iphonesimulator*]=arm64"]
|
|
369
|
+
|
|
370
|
+
build_exit = run_to_file(build_cmd_retry, cwd: pod_install_root, env: { "HOME" => real_home }, log_path: build_log_retry)
|
|
371
|
+
build_log = build_log_retry
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
unless build_exit.zero?
|
|
365
375
|
File.open(status_md, "a") do |f|
|
|
366
|
-
f.puts "-
|
|
376
|
+
f.puts "- build: 失败(exit=#{build_exit})"
|
|
367
377
|
f.puts
|
|
368
378
|
f.puts "**xcodebuild 日志(尾部)**"
|
|
369
379
|
f.puts "```"
|
|
370
|
-
tail_lines(
|
|
380
|
+
tail_lines(build_log, 200).each { |line| f.puts line }
|
|
371
381
|
f.puts "```"
|
|
372
382
|
end
|
|
373
|
-
|
|
383
|
+
puts "[docc] -------- build 日志(尾部 200 行)--------"
|
|
384
|
+
puts tail_lines(build_log, 200).join("\n")
|
|
385
|
+
fail!("build 失败")
|
|
374
386
|
end
|
|
375
387
|
|
|
376
|
-
File.open(status_md, "a") { |f| f.puts "-
|
|
388
|
+
File.open(status_md, "a") { |f| f.puts "- build: 成功" }
|
|
377
389
|
|
|
390
|
+
# xcodebuild build + BUILD_DOCUMENTATION_DURING_EACH_BUILD=YES 会直接生成 .doccarchive
|
|
391
|
+
# 先尝试找现成的 archive
|
|
378
392
|
archives = []
|
|
379
393
|
Find.find(derived_dir) do |p|
|
|
380
394
|
next unless File.directory?(p)
|
|
@@ -390,43 +404,58 @@ module BBItools
|
|
|
390
404
|
.reverse
|
|
391
405
|
.find { |p| File.basename(p) == "#{scheme}.doccarchive" } || archives.max_by { |p| File.mtime(p) }
|
|
392
406
|
|
|
393
|
-
|
|
407
|
+
# 若 build 没直接产出 .doccarchive,则找 symbol-graph 目录手动跑 xcrun docc convert
|
|
408
|
+
if doccarchive_path.to_s.strip.empty?
|
|
409
|
+
log "build 未直接生成 .doccarchive,尝试手动 docc convert..."
|
|
394
410
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
411
|
+
# 优先使用 -emit-symbol-graph-dir 指定的目录(swiftc 直接写入)
|
|
412
|
+
symbol_graph_dir = nil
|
|
413
|
+
if Dir.glob(File.join(symbol_graph_out, "*.symbols.json")).any?
|
|
414
|
+
symbol_graph_dir = symbol_graph_out
|
|
415
|
+
log "使用 swiftc -emit-symbol-graph 输出: #{symbol_graph_dir}"
|
|
416
|
+
end
|
|
400
417
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
manual_archive_dir = File.join(derived_data_root, ".manual_doccarchive", component_name, "#{scheme}.doccarchive")
|
|
408
|
-
FileUtils.rm_rf(manual_archive_dir)
|
|
409
|
-
FileUtils.mkdir_p(File.dirname(manual_archive_dir))
|
|
410
|
-
|
|
411
|
-
manual_log = File.join(logs_dir, "docc-convert-manual.log")
|
|
412
|
-
manual_diag = File.join(logs_dir, "docc-convert-manual-diagnostics.json")
|
|
413
|
-
|
|
414
|
-
manual_cmd = [
|
|
415
|
-
"xcrun", "docc", "convert", docc_catalog_abs,
|
|
416
|
-
"--emit-lmdb-index",
|
|
417
|
-
"--fallback-display-name", scheme,
|
|
418
|
-
"--fallback-bundle-identifier", "org.cocoapods.#{scheme}",
|
|
419
|
-
"--output-dir", manual_archive_dir,
|
|
420
|
-
"--additional-symbol-graph-dir", symbol_graph_dir,
|
|
421
|
-
"--ide-console-output",
|
|
422
|
-
"--diagnostics-file", manual_diag
|
|
423
|
-
]
|
|
424
|
-
|
|
425
|
-
manual_exit = run_to_file(manual_cmd, cwd: podspec_dir_abs, env: { "HOME" => real_home }, log_path: manual_log)
|
|
426
|
-
fail!("docc convert 失败(exit=#{manual_exit})") unless manual_exit.zero? && File.directory?(manual_archive_dir)
|
|
427
|
-
|
|
428
|
-
doccarchive_path = manual_archive_dir
|
|
418
|
+
# 备用:Xcode 中间目录(BUILD_DOCUMENTATION_DURING_EACH_BUILD=YES 生成的路径)
|
|
419
|
+
if symbol_graph_dir.to_s.strip.empty?
|
|
420
|
+
symbol_graph_dir = Dir.glob(
|
|
421
|
+
File.join(derived_dir, "Build", "Intermediates.noindex", "**", "#{scheme}.build", "symbol-graph")
|
|
422
|
+
).sort.first
|
|
429
423
|
end
|
|
424
|
+
|
|
425
|
+
fail!("未找到 symbol-graph 文件(#{symbol_graph_out} 和 #{scheme}.build/symbol-graph 均为空):确认 pod 有 Swift 源码且 OTHER_SWIFT_FLAGS 已生效") if symbol_graph_dir.to_s.strip.empty?
|
|
426
|
+
|
|
427
|
+
manual_archive_dir = File.join(derived_data_root, ".manual_doccarchive", component_name, "#{scheme}.doccarchive")
|
|
428
|
+
FileUtils.rm_rf(manual_archive_dir)
|
|
429
|
+
FileUtils.mkdir_p(File.dirname(manual_archive_dir))
|
|
430
|
+
|
|
431
|
+
docc_convert_log = File.join(logs_dir, "docc-convert.log")
|
|
432
|
+
docc_convert_diag = File.join(logs_dir, "docc-convert-diagnostics.json")
|
|
433
|
+
|
|
434
|
+
docc_convert_cmd = [
|
|
435
|
+
"xcrun", "docc", "convert",
|
|
436
|
+
"--emit-lmdb-index",
|
|
437
|
+
"--fallback-display-name", scheme,
|
|
438
|
+
"--fallback-bundle-identifier", "org.cocoapods.#{scheme}",
|
|
439
|
+
"--fallback-bundle-version", spec_version.to_s.strip.empty? ? "1.0" : spec_version,
|
|
440
|
+
"--output-dir", manual_archive_dir,
|
|
441
|
+
"--additional-symbol-graph-dir", symbol_graph_dir,
|
|
442
|
+
"--ide-console-output",
|
|
443
|
+
"--diagnostics-file", docc_convert_diag
|
|
444
|
+
]
|
|
445
|
+
docc_convert_cmd.insert(3, docc_catalog_abs) if docc_catalog_abs
|
|
446
|
+
|
|
447
|
+
log "执行 docc convert: #{scheme}"
|
|
448
|
+
File.open(status_md, "a") { |f| f.puts "- docc_convert: #{docc_convert_cmd.join(" ")}" }
|
|
449
|
+
|
|
450
|
+
docc_convert_exit = run_to_file(docc_convert_cmd, cwd: pod_install_root, env: { "HOME" => real_home }, log_path: docc_convert_log)
|
|
451
|
+
|
|
452
|
+
unless docc_convert_exit.zero? && File.directory?(manual_archive_dir)
|
|
453
|
+
puts "[docc] -------- docc-convert 日志(尾部 100 行)--------"
|
|
454
|
+
puts tail_lines(docc_convert_log, 100).join("\n")
|
|
455
|
+
fail!("docc convert 失败(exit=#{docc_convert_exit})")
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
doccarchive_path = manual_archive_dir
|
|
430
459
|
end
|
|
431
460
|
|
|
432
461
|
doccarchive_rel = rel_path(doccarchive_path, base_dir: base_dir)
|
|
@@ -437,23 +466,19 @@ module BBItools
|
|
|
437
466
|
|
|
438
467
|
export_log = File.join(logs_dir, "docc-transform.log")
|
|
439
468
|
|
|
440
|
-
doccarchive_for_export = rel_path(doccarchive_path, base_dir: podspec_dir_abs)
|
|
441
469
|
export_staging_dir = File.join(derived_data_root, ".docc-site", scheme)
|
|
442
470
|
FileUtils.rm_rf(export_staging_dir)
|
|
443
471
|
FileUtils.mkdir_p(export_staging_dir)
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
# Export Command
|
|
472
|
+
|
|
473
|
+
# Export Command (使用绝对路径)
|
|
447
474
|
export_cmd = [
|
|
448
475
|
"xcrun", "docc", "process-archive", "transform-for-static-hosting",
|
|
449
|
-
|
|
450
|
-
"--output-path",
|
|
451
|
-
"--hosting-base-path", hosting_base_path
|
|
476
|
+
doccarchive_path,
|
|
477
|
+
"--output-path", export_staging_dir,
|
|
478
|
+
"--hosting-base-path", hosting_base_path
|
|
452
479
|
]
|
|
453
480
|
|
|
454
|
-
export_cmd_report = export_cmd.
|
|
455
|
-
export_cmd_report[export_cmd_report.index(doccarchive_for_export) || 0] = doccarchive_rel
|
|
456
|
-
export_cmd_report[export_cmd_report.index(site_out_for_export) || 0] = rel_path(site_out, base_dir: base_dir)
|
|
481
|
+
export_cmd_report = export_cmd.map { |arg| arg == doccarchive_path ? doccarchive_rel : (arg == export_staging_dir ? rel_path(site_out, base_dir: base_dir) : arg) }
|
|
457
482
|
File.open(status_md, "a") { |f| f.puts "- export: #{export_cmd_report.join(" ")}" }
|
|
458
483
|
|
|
459
484
|
log "导出静态站点: #{component_dir_rel} (hosting-base-path: #{hosting_base_path})"
|
|
@@ -470,7 +495,7 @@ module BBItools
|
|
|
470
495
|
|
|
471
496
|
File.open(status_md, "a") { |f| f.puts "- docc_html_dir: #{docc_html_dir.empty? ? '(builtin)' : docc_html_dir}" }
|
|
472
497
|
|
|
473
|
-
export_exit = run_to_file(export_cmd, cwd:
|
|
498
|
+
export_exit = run_to_file(export_cmd, cwd: pod_install_root, env: export_env, log_path: export_log)
|
|
474
499
|
unless export_exit.zero?
|
|
475
500
|
warn tail_lines(export_log, 200).join("\n")
|
|
476
501
|
fail!("docc 导出失败")
|
|
@@ -582,6 +607,201 @@ module BBItools
|
|
|
582
607
|
exit 1
|
|
583
608
|
end
|
|
584
609
|
|
|
610
|
+
# 扫描 pods_dir 下每个 Pod,找出拥有多个 .docc catalog 的 target
|
|
611
|
+
# 保留与 Pod 名同名的那个(或第一个),删除其余的,同时从 Pods.xcodeproj/project.pbxproj 移除对应引用
|
|
612
|
+
def remove_extra_docc_catalogs(pods_dir)
|
|
613
|
+
removed = []
|
|
614
|
+
return removed unless File.directory?(pods_dir)
|
|
615
|
+
|
|
616
|
+
pbxproj_path = File.join(pods_dir, "Pods.xcodeproj", "project.pbxproj")
|
|
617
|
+
|
|
618
|
+
Dir.children(pods_dir).each do |pod_name|
|
|
619
|
+
pod_path = File.join(pods_dir, pod_name)
|
|
620
|
+
next unless File.directory?(pod_path)
|
|
621
|
+
next if pod_name.start_with?(".")
|
|
622
|
+
|
|
623
|
+
docc_dirs = []
|
|
624
|
+
Find.find(pod_path) do |p|
|
|
625
|
+
if File.directory?(p) && p.end_with?(".docc")
|
|
626
|
+
docc_dirs << p
|
|
627
|
+
Find.prune
|
|
628
|
+
end
|
|
629
|
+
end
|
|
630
|
+
|
|
631
|
+
next if docc_dirs.size <= 1
|
|
632
|
+
|
|
633
|
+
# 优先保留与 Pod 名同名的 catalog,否则保留第一个
|
|
634
|
+
preferred = docc_dirs.find { |p| File.basename(p, ".docc").casecmp(pod_name).zero? }
|
|
635
|
+
preferred ||= docc_dirs.first
|
|
636
|
+
|
|
637
|
+
docc_dirs.each do |d|
|
|
638
|
+
next if d == preferred
|
|
639
|
+
log "🗑 删除多余 .docc catalog: #{d}"
|
|
640
|
+
FileUtils.rm_rf(d)
|
|
641
|
+
# pbxproj 里仍有文件引用,xcodebuild 会从项目文件读 catalog 列表,必须一并清理
|
|
642
|
+
remove_docc_from_pbxproj(pbxproj_path, File.basename(d)) if File.file?(pbxproj_path)
|
|
643
|
+
removed << d
|
|
644
|
+
end
|
|
645
|
+
end
|
|
646
|
+
|
|
647
|
+
removed
|
|
648
|
+
rescue StandardError => e
|
|
649
|
+
log "⚠️ remove_extra_docc_catalogs 异常: #{e.message}"
|
|
650
|
+
[]
|
|
651
|
+
end
|
|
652
|
+
|
|
653
|
+
# 从 project.pbxproj 中删除对指定 .docc catalog 的所有引用
|
|
654
|
+
# 通过 GUID 匹配,移除 PBXFileReference / PBXBuildFile / group children / build phase files 各行
|
|
655
|
+
def remove_docc_from_pbxproj(pbxproj_path, docc_basename)
|
|
656
|
+
text = safe_read_text(pbxproj_path)
|
|
657
|
+
return if text.empty?
|
|
658
|
+
|
|
659
|
+
# 找 PBXFileReference GUID
|
|
660
|
+
file_ref_guids = []
|
|
661
|
+
text.scan(/(\h{32})\s+\/\*\s+#{Regexp.escape(docc_basename)}\s+\*\/\s*=\s*\{isa\s*=\s*PBXFileReference/) do
|
|
662
|
+
file_ref_guids << $1
|
|
663
|
+
end
|
|
664
|
+
return if file_ref_guids.empty?
|
|
665
|
+
|
|
666
|
+
# 找引用这些 fileRef 的 PBXBuildFile GUID
|
|
667
|
+
build_file_guids = []
|
|
668
|
+
file_ref_guids.each do |ref_guid|
|
|
669
|
+
text.scan(/(\h{32})\s+\/\*[^*]*\*\/\s*=\s*\{isa\s*=\s*PBXBuildFile;\s*fileRef\s*=\s*#{ref_guid}/) do
|
|
670
|
+
build_file_guids << $1
|
|
671
|
+
end
|
|
672
|
+
end
|
|
673
|
+
|
|
674
|
+
all_guids = (file_ref_guids + build_file_guids).uniq
|
|
675
|
+
|
|
676
|
+
# 删除所有包含这些 GUID 的行(覆盖 definition / children / files 引用)
|
|
677
|
+
new_text = text.lines.reject { |line| all_guids.any? { |guid| line.include?(guid) } }.join
|
|
678
|
+
File.write(pbxproj_path, new_text) unless new_text == text
|
|
679
|
+
rescue StandardError => e
|
|
680
|
+
log "⚠️ remove_docc_from_pbxproj 异常: #{e.message}"
|
|
681
|
+
end
|
|
682
|
+
|
|
683
|
+
def write_minimal_xcodeproj(xcodeproj_dir, deployment_target)
|
|
684
|
+
FileUtils.mkdir_p(xcodeproj_dir)
|
|
685
|
+
pbxproj = <<~PBXPROJ
|
|
686
|
+
// !$*UTF8*$!
|
|
687
|
+
{
|
|
688
|
+
archiveVersion = 1;
|
|
689
|
+
classes = {
|
|
690
|
+
};
|
|
691
|
+
objectVersion = 56;
|
|
692
|
+
objects = {
|
|
693
|
+
/* Begin PBXFileReference section */
|
|
694
|
+
00000001 /* App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
695
|
+
/* End PBXFileReference section */
|
|
696
|
+
|
|
697
|
+
/* Begin PBXGroup section */
|
|
698
|
+
00000002 = {
|
|
699
|
+
isa = PBXGroup;
|
|
700
|
+
children = (
|
|
701
|
+
00000003,
|
|
702
|
+
);
|
|
703
|
+
sourceTree = "<group>";
|
|
704
|
+
};
|
|
705
|
+
00000003 /* Products */ = {
|
|
706
|
+
isa = PBXGroup;
|
|
707
|
+
children = (
|
|
708
|
+
00000001,
|
|
709
|
+
);
|
|
710
|
+
name = Products;
|
|
711
|
+
sourceTree = "<group>";
|
|
712
|
+
};
|
|
713
|
+
/* End PBXGroup section */
|
|
714
|
+
|
|
715
|
+
/* Begin PBXNativeTarget section */
|
|
716
|
+
00000004 /* App */ = {
|
|
717
|
+
isa = PBXNativeTarget;
|
|
718
|
+
buildConfigurationList = 00000005;
|
|
719
|
+
buildPhases = ();
|
|
720
|
+
buildRules = ();
|
|
721
|
+
dependencies = ();
|
|
722
|
+
name = App;
|
|
723
|
+
productName = App;
|
|
724
|
+
productReference = 00000001;
|
|
725
|
+
productType = "com.apple.product-type.application";
|
|
726
|
+
};
|
|
727
|
+
/* End PBXNativeTarget section */
|
|
728
|
+
|
|
729
|
+
/* Begin PBXProject section */
|
|
730
|
+
00000006 /* Project object */ = {
|
|
731
|
+
isa = PBXProject;
|
|
732
|
+
buildConfigurationList = 00000007;
|
|
733
|
+
compatibilityVersion = "Xcode 14.0";
|
|
734
|
+
developmentRegion = en;
|
|
735
|
+
hasScannedForEncodings = 0;
|
|
736
|
+
knownRegions = (en, Base);
|
|
737
|
+
mainGroup = 00000002;
|
|
738
|
+
productRefGroup = 00000003;
|
|
739
|
+
projectDirPath = "";
|
|
740
|
+
projectRoot = "";
|
|
741
|
+
targets = (00000004);
|
|
742
|
+
};
|
|
743
|
+
/* End PBXProject section */
|
|
744
|
+
|
|
745
|
+
/* Begin XCBuildConfiguration section */
|
|
746
|
+
00000008 /* Debug */ = {
|
|
747
|
+
isa = XCBuildConfiguration;
|
|
748
|
+
buildSettings = {
|
|
749
|
+
ALWAYS_SEARCH_USER_PATHS = NO;
|
|
750
|
+
IPHONEOS_DEPLOYMENT_TARGET = #{deployment_target};
|
|
751
|
+
SDKROOT = iphoneos;
|
|
752
|
+
};
|
|
753
|
+
name = Debug;
|
|
754
|
+
};
|
|
755
|
+
00000009 /* Release */ = {
|
|
756
|
+
isa = XCBuildConfiguration;
|
|
757
|
+
buildSettings = {
|
|
758
|
+
ALWAYS_SEARCH_USER_PATHS = NO;
|
|
759
|
+
IPHONEOS_DEPLOYMENT_TARGET = #{deployment_target};
|
|
760
|
+
SDKROOT = iphoneos;
|
|
761
|
+
};
|
|
762
|
+
name = Release;
|
|
763
|
+
};
|
|
764
|
+
0000000A /* Debug */ = {
|
|
765
|
+
isa = XCBuildConfiguration;
|
|
766
|
+
buildSettings = {
|
|
767
|
+
INFOPLIST_FILE = "";
|
|
768
|
+
PRODUCT_BUNDLE_IDENTIFIER = org.example.App;
|
|
769
|
+
PRODUCT_NAME = App;
|
|
770
|
+
};
|
|
771
|
+
name = Debug;
|
|
772
|
+
};
|
|
773
|
+
0000000B /* Release */ = {
|
|
774
|
+
isa = XCBuildConfiguration;
|
|
775
|
+
buildSettings = {
|
|
776
|
+
INFOPLIST_FILE = "";
|
|
777
|
+
PRODUCT_BUNDLE_IDENTIFIER = org.example.App;
|
|
778
|
+
PRODUCT_NAME = App;
|
|
779
|
+
};
|
|
780
|
+
name = Release;
|
|
781
|
+
};
|
|
782
|
+
/* End XCBuildConfiguration section */
|
|
783
|
+
|
|
784
|
+
/* Begin XCConfigurationList section */
|
|
785
|
+
00000005 = {
|
|
786
|
+
isa = XCConfigurationList;
|
|
787
|
+
buildConfigurations = (0000000A, 0000000B);
|
|
788
|
+
defaultConfigurationIsVisible = 0;
|
|
789
|
+
defaultConfigurationName = Release;
|
|
790
|
+
};
|
|
791
|
+
00000007 = {
|
|
792
|
+
isa = XCConfigurationList;
|
|
793
|
+
buildConfigurations = (00000008, 00000009);
|
|
794
|
+
defaultConfigurationIsVisible = 0;
|
|
795
|
+
defaultConfigurationName = Release;
|
|
796
|
+
};
|
|
797
|
+
/* End XCConfigurationList section */
|
|
798
|
+
};
|
|
799
|
+
rootObject = 00000006 /* Project object */;
|
|
800
|
+
}
|
|
801
|
+
PBXPROJ
|
|
802
|
+
File.write(File.join(xcodeproj_dir, "project.pbxproj"), pbxproj)
|
|
803
|
+
end
|
|
804
|
+
|
|
585
805
|
def abs_path(path, base_dir:)
|
|
586
806
|
return "" if path.nil? || path.empty?
|
|
587
807
|
return File.expand_path(path) if path.start_with?("/")
|
|
@@ -968,38 +1188,6 @@ module BBItools
|
|
|
968
1188
|
push_exit
|
|
969
1189
|
end
|
|
970
1190
|
|
|
971
|
-
def find_workspace_from_lint_log(lint_log, lint_root)
|
|
972
|
-
workspace_path = nil
|
|
973
|
-
|
|
974
|
-
File.foreach(lint_log, encoding: "UTF-8", invalid: :replace, undef: :replace, replace: "?") do |line|
|
|
975
|
-
if (m = line.match(/Pods workspace available at `([^`]+)`/))
|
|
976
|
-
workspace_path = m[1].to_s.strip
|
|
977
|
-
elsif (m = line.match(%r{(/[^ ]+App\.xcworkspace)}))
|
|
978
|
-
workspace_path = m[1].to_s.strip
|
|
979
|
-
end
|
|
980
|
-
end
|
|
981
|
-
|
|
982
|
-
if workspace_path && File.directory?(workspace_path)
|
|
983
|
-
return workspace_path
|
|
984
|
-
end
|
|
985
|
-
|
|
986
|
-
candidates = []
|
|
987
|
-
root = File.expand_path(lint_root)
|
|
988
|
-
root_depth = root.split(File::SEPARATOR).length
|
|
989
|
-
|
|
990
|
-
Find.find(root) do |p|
|
|
991
|
-
if File.directory?(p) && p.end_with?(".xcworkspace")
|
|
992
|
-
depth = p.split(File::SEPARATOR).length - root_depth
|
|
993
|
-
candidates << p if depth <= 4
|
|
994
|
-
Find.prune
|
|
995
|
-
end
|
|
996
|
-
end
|
|
997
|
-
|
|
998
|
-
candidates
|
|
999
|
-
.select { |p| File.directory?(p) }
|
|
1000
|
-
.max_by { |p| File.mtime(p) }
|
|
1001
|
-
end
|
|
1002
|
-
|
|
1003
1191
|
def bump_deployment_targets_in_validation_dir(validation_dir, min_target)
|
|
1004
1192
|
min_v = parse_version(min_target)
|
|
1005
1193
|
pattern = /(IPHONEOS_DEPLOYMENT_TARGET\s*=\s*)([0-9]+(?:\.[0-9]+)?)(\s*;?)/
|