build_xcframework.sh 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. #!/bin/bash
  2. set -e
  3. : "${OPENSWIFTUI_LIBRARY_TYPE:=dynamic}"
  4. : "${OPENSWIFTUI_USE_LOCAL_DEPS:=1}"
  5. : "${OPENSWIFTUI_SWIFTUI_RENDERER:=0}"
  6. export OPENSWIFTUI_LIBRARY_TYPE
  7. export OPENSWIFTUI_USE_LOCAL_DEPS
  8. export OPENSWIFTUI_SWIFTUI_RENDERER
  9. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd -P)"
  10. PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
  11. cd "$PROJECT_ROOT"
  12. PROJECT_BUILD_DIR="${PROJECT_BUILD_DIR:-"${PROJECT_ROOT}/build"}"
  13. DERIVED_DATA_PATH="$PROJECT_BUILD_DIR/DerivedData"
  14. XCODEPROJ="$PROJECT_ROOT/OpenSwiftUI.xcodeproj"
  15. XCODEWORKSPACE="$PROJECT_ROOT/Workspace.xcworkspace"
  16. # Parse arguments.
  17. # --sdk and --archs are paired: --sdk <sdk> --archs <arch1,arch2>
  18. # If --archs is omitted for an SDK, all default architectures are built.
  19. SDKS=()
  20. SDK_ARCHS=()
  21. DEBUG_MODE=false
  22. RUN_TUIST_INSTALL=true
  23. EXPLICIT_FRAMEWORK_NAMES=false
  24. FRAMEWORK_NAMES=()
  25. DEFAULT_FRAMEWORK_NAMES=(
  26. "OpenAttributeGraphShims"
  27. "OpenCoreGraphicsShims"
  28. "OpenQuartzCoreShims"
  29. "OpenObservation"
  30. "OpenRenderBoxShims"
  31. "OpenSwiftUICore"
  32. "OpenSwiftUI"
  33. )
  34. print_usage() {
  35. cat <<'USAGE'
  36. Usage: Scripts/build_xcframework.sh [options] [framework ...]
  37. Options:
  38. --sdk <sdk> Build for an SDK. May be passed multiple times.
  39. --archs <arch1,arch2> Override architectures for the previous --sdk.
  40. --debug Keep release metadata and copy dSYMs.
  41. --compute Build OpenAttributeGraphShims with the Compute source backend.
  42. --skip-tuist-install Skip tuist install.
  43. --framework <name> Build one framework. May be passed multiple times.
  44. --help Show this help.
  45. USAGE
  46. }
  47. while [[ $# -gt 0 ]]; do
  48. case "$1" in
  49. --help|-h)
  50. print_usage
  51. exit 0
  52. ;;
  53. --sdk)
  54. SDKS+=("$2")
  55. SDK_ARCHS+=("")
  56. shift 2
  57. ;;
  58. --archs)
  59. if [ ${#SDKS[@]} -gt 0 ]; then
  60. SDK_ARCHS[$((${#SDK_ARCHS[@]} - 1))]="$2"
  61. fi
  62. shift 2
  63. ;;
  64. --debug)
  65. DEBUG_MODE=true
  66. shift
  67. ;;
  68. --compute)
  69. OPENSWIFTUI_OPENATTRIBUTESHIMS_ATTRIBUTEGRAPH=0
  70. OPENSWIFTUI_OPENATTRIBUTESHIMS_DANCEUIGRAPH=0
  71. OPENSWIFTUI_OPENATTRIBUTESHIMS_COMPUTE=1
  72. OPENSWIFTUI_OPENATTRIBUTESHIMS_COMPUTE_BINARY=0
  73. export OPENSWIFTUI_OPENATTRIBUTESHIMS_ATTRIBUTEGRAPH
  74. export OPENSWIFTUI_OPENATTRIBUTESHIMS_DANCEUIGRAPH
  75. export OPENSWIFTUI_OPENATTRIBUTESHIMS_COMPUTE
  76. export OPENSWIFTUI_OPENATTRIBUTESHIMS_COMPUTE_BINARY
  77. shift
  78. ;;
  79. --skip-tuist-install)
  80. RUN_TUIST_INSTALL=false
  81. shift
  82. ;;
  83. --framework)
  84. EXPLICIT_FRAMEWORK_NAMES=true
  85. FRAMEWORK_NAMES+=("$2")
  86. shift 2
  87. ;;
  88. *)
  89. FRAMEWORK_NAMES+=("$1")
  90. shift
  91. ;;
  92. esac
  93. done
  94. # By default, build the full binary distribution set. Keep
  95. # `Scripts/build_xcframework.sh OpenSwiftUI` compatible with the historical CI
  96. # invocation while switching it to the multi-xcframework distribution.
  97. if [ ${#FRAMEWORK_NAMES[@]} -eq 0 ] ||
  98. ([ "$EXPLICIT_FRAMEWORK_NAMES" = false ] &&
  99. [ ${#FRAMEWORK_NAMES[@]} -eq 1 ] &&
  100. [ "${FRAMEWORK_NAMES[0]}" = "OpenSwiftUI" ]); then
  101. FRAMEWORK_NAMES=("${DEFAULT_FRAMEWORK_NAMES[@]}")
  102. fi
  103. # Default: macosx and iphonesimulator. Compute builds also include iphoneos.
  104. # Note: iphoneos SDK support is blocked by an AG issue. See #835.
  105. if [ ${#SDKS[@]} -eq 0 ]; then
  106. if [ "${OPENSWIFTUI_OPENATTRIBUTESHIMS_COMPUTE:-0}" = "1" ]; then
  107. SDKS=("macosx" "iphonesimulator" "iphoneos")
  108. SDK_ARCHS=("" "" "")
  109. else
  110. SDKS=("macosx" "iphonesimulator")
  111. SDK_ARCHS=("" "")
  112. fi
  113. fi
  114. if [ "${OPENSWIFTUI_SKIP_TUIST_INSTALL:-0}" = "1" ]; then
  115. RUN_TUIST_INSTALL=false
  116. fi
  117. if ! command -v tuist >/dev/null 2>&1; then
  118. echo "Error: tuist is required to generate $XCODEPROJ."
  119. exit 1
  120. fi
  121. if [ "$RUN_TUIST_INSTALL" = true ]; then
  122. echo "Installing Tuist dependencies..."
  123. tuist install
  124. else
  125. echo "Skipping tuist install."
  126. fi
  127. echo "Generating Xcode project with Tuist..."
  128. tuist generate --no-open
  129. if [ ! -d "$XCODEPROJ" ]; then
  130. echo "Error: Expected Tuist to generate $XCODEPROJ."
  131. exit 1
  132. fi
  133. if [ ! -d "$XCODEWORKSPACE" ]; then
  134. echo "Error: Expected Tuist to generate $XCODEWORKSPACE."
  135. exit 1
  136. fi
  137. remove_generated_macro_references() {
  138. local project_path="$1"
  139. local target_name="$2"
  140. local macro_name="$3"
  141. if [ ! -f "$project_path/project.pbxproj" ]; then
  142. return
  143. fi
  144. ruby - "$project_path/project.pbxproj" "$target_name" "$macro_name" <<'RUBY'
  145. path, target_name, macro_name = ARGV
  146. text = File.read(path)
  147. object_pattern = /^ ([A-Za-z0-9]+) \/\* [^*]+ \*\/ = \{\n.*?^ \};\n/m
  148. dependency_names = {}
  149. text.scan(object_pattern) do |capture|
  150. id = Array(capture).first
  151. block = Regexp.last_match(0)
  152. next unless block.include?("isa = PBXTargetDependency;")
  153. dependency_names[id] =
  154. block[/^ name = ([^;]+);$/, 1] ||
  155. block[/^ target = [A-Za-z0-9]+ \/\* ([^*]+) \*\//, 1]
  156. end
  157. macro_phase_ids = []
  158. text.scan(object_pattern) do |capture|
  159. id = Array(capture).first
  160. block = Regexp.last_match(0)
  161. next unless block.include?("isa = PBXShellScriptBuildPhase;")
  162. next unless block.include?(macro_name)
  163. macro_phase_ids << id
  164. end
  165. macro_target_ids = []
  166. text.scan(object_pattern) do |capture|
  167. id = Array(capture).first
  168. block = Regexp.last_match(0)
  169. next unless block.include?("isa = PBXNativeTarget;")
  170. next unless block.match?(/^ name = #{Regexp.escape(macro_name)};$/)
  171. macro_target_ids << id
  172. end
  173. target_dependency_ids = []
  174. text.scan(object_pattern) do |_capture|
  175. block = Regexp.last_match(0)
  176. next unless block.include?("isa = PBXNativeTarget;")
  177. next unless block.match?(/^ name = #{Regexp.escape(target_name)};$/)
  178. dependencies = block[/^ dependencies = \(\n(.*?)^ \);$/m, 1]
  179. target_dependency_ids = dependencies.to_s.scan(/^\s+([A-Za-z0-9]+) \/\* PBXTargetDependency \*\/,/).flatten
  180. break
  181. end
  182. dependency_ids_to_remove = target_dependency_ids.select { |id| dependency_names[id] == macro_name }
  183. # Tuist can leave a standalone macro PBXTargetDependency in derived projects even
  184. # when the framework target does not list it in `dependencies`. Xcode's automatic
  185. # scheme discovery can still pull that target into archive builds, so prune it
  186. # when it is unambiguous.
  187. if dependency_ids_to_remove.empty?
  188. matching_ids = dependency_names.select { |_id, name| name == macro_name }.keys
  189. dependency_ids_to_remove = matching_ids if matching_ids.one?
  190. end
  191. changed = false
  192. dependency_ids_to_remove.each do |id|
  193. text.gsub!(/^ #{Regexp.escape(id)} \/\* PBXTargetDependency \*\/,\n/, "")
  194. removed = text.gsub!(/^ #{Regexp.escape(id)} \/\* PBXTargetDependency \*\/ = \{\n.*?^ \};\n/m, "")
  195. changed ||= !!removed
  196. end
  197. macro_phase_ids.each do |id|
  198. text.gsub!(/^ #{Regexp.escape(id)} \/\* [^*]+ \*\/,\n/, "")
  199. removed = text.gsub!(/^ #{Regexp.escape(id)} \/\* [^*]+ \*\/ = \{\n.*?^ \};\n/m, "")
  200. changed ||= !!removed
  201. end
  202. macro_target_ids.each do |id|
  203. removed = text.gsub!(/^ #{Regexp.escape(id)} \/\* #{Regexp.escape(macro_name)} \*\/,\n/, "")
  204. changed ||= !!removed
  205. end
  206. plugin_path = '$BUILD_DIR/Debug$EFFECTIVE_PLATFORM_NAME/' + macro_name + '#' + macro_name
  207. plugin_path_pattern = Regexp.escape(plugin_path)
  208. removed_flags = text.gsub!(
  209. /^(\t+)"-load-plugin-executable",\n\1"#{plugin_path_pattern}",\n/,
  210. ""
  211. )
  212. changed ||= !!removed_flags
  213. exit unless changed
  214. File.write(path, text)
  215. puts "Removed #{macro_name} archive references from #{File.dirname(path)}"
  216. RUBY
  217. }
  218. # The xcframework archive path sets OPENSWIFTUI_XCFRAMEWORK_BUILD, which expands
  219. # macro usages inline where needed. Avoid forcing generated macro tool targets to
  220. # archive for simulator SDKs, where Xcode can try to build them for the target
  221. # platform instead of the host platform.
  222. remove_generated_macro_references "$PROJECT_ROOT/.build/tuist-derived/OpenObservation/OpenObservation.xcodeproj" "OpenObservation" "OpenObservationMacros"
  223. remove_generated_macro_references "$PROJECT_ROOT/../OpenObservation/OpenObservation.xcodeproj" "OpenObservation" "OpenObservationMacros"
  224. remove_generated_macro_references "$XCODEPROJ" "OpenSwiftUICore" "OpenSwiftUIMacros"
  225. remove_generated_macro_references "$XCODEPROJ" "OpenSwiftUICore" "OpenObservationMacros"
  226. echo "SDKs: ${SDKS[*]}"
  227. for i in "${!SDKS[@]}"; do
  228. if [ -n "${SDK_ARCHS[$i]}" ]; then
  229. echo " ${SDKS[$i]}: ARCHS=${SDK_ARCHS[$i]}"
  230. else
  231. echo " ${SDKS[$i]}: (default archs)"
  232. fi
  233. done
  234. echo "Debug mode: $DEBUG_MODE"
  235. echo "Frameworks: ${FRAMEWORK_NAMES[*]}"
  236. sdk_destination() {
  237. case "$1" in
  238. iphonesimulator) echo "generic/platform=iOS Simulator" ;;
  239. iphoneos) echo "generic/platform=iOS" ;;
  240. macosx) echo "generic/platform=macOS" ;;
  241. *) echo "generic/platform=$1" ;;
  242. esac
  243. }
  244. framework_path() {
  245. local archive_path="$1"
  246. local scheme="$2"
  247. echo "$archive_path/Products/Library/Frameworks/$scheme.framework"
  248. }
  249. xcframework_path() {
  250. local scheme="$1"
  251. echo "$PROJECT_BUILD_DIR/$scheme.xcframework"
  252. }
  253. framework_modules_path() {
  254. local framework="$1"
  255. if [ -d "$framework/Versions" ]; then
  256. echo "$framework/Versions/Current/Modules"
  257. else
  258. echo "$framework/Modules"
  259. fi
  260. }
  261. strip_release_metadata() {
  262. local modules_path="$1"
  263. if [ "$DEBUG_MODE" = false ]; then
  264. find "$modules_path" -name "*.abi.json" -delete
  265. find "$modules_path" -name "*.package.swiftinterface" -delete
  266. find "$modules_path" -name "*.private.swiftinterface" -delete
  267. fi
  268. }
  269. first_existing_project() {
  270. local candidate
  271. for candidate in "$@"; do
  272. if [ -d "$candidate" ]; then
  273. echo "$candidate"
  274. return
  275. fi
  276. done
  277. echo "$1"
  278. }
  279. project_args_for_scheme() {
  280. local scheme="$1"
  281. case "$scheme" in
  282. COpenSwiftUI|OpenSwiftUI|OpenSwiftUICore|OpenSwiftUI_SPI|OpenSwiftUISymbolDualTestsSupport)
  283. echo "-workspace" "$XCODEWORKSPACE"
  284. ;;
  285. _AttributeGraphDeviceSwiftShims)
  286. echo "-project" "$(first_existing_project "$PROJECT_ROOT/../DarwinPrivateFrameworks/DarwinPrivateFrameworks.xcodeproj" "$PROJECT_ROOT/.build/tuist-derived/DarwinPrivateFrameworks/DarwinPrivateFrameworks.xcodeproj")"
  287. ;;
  288. OpenAttributeGraphShims)
  289. echo "-project" "$(first_existing_project "$PROJECT_ROOT/../OpenAttributeGraph/OpenAttributeGraph.xcodeproj" "$PROJECT_ROOT/.build/tuist-derived/OpenAttributeGraph/OpenAttributeGraph.xcodeproj")"
  290. ;;
  291. OpenCoreGraphics|OpenCoreGraphicsShims|OpenQuartzCore|OpenQuartzCoreShims)
  292. echo "-project" "$(first_existing_project "$PROJECT_ROOT/../OpenCoreGraphics/OpenCoreGraphics.xcodeproj" "$PROJECT_ROOT/.build/tuist-derived/OpenCoreGraphics/OpenCoreGraphics.xcodeproj")"
  293. ;;
  294. OpenObservation|OpenObservationCxx)
  295. echo "-project" "$(first_existing_project "$PROJECT_ROOT/../OpenObservation/OpenObservation.xcodeproj" "$PROJECT_ROOT/.build/tuist-derived/OpenObservation/OpenObservation.xcodeproj")"
  296. ;;
  297. OpenRenderBoxShims)
  298. echo "-project" "$(first_existing_project "$PROJECT_ROOT/../OpenRenderBox/OpenRenderBox.xcodeproj" "$PROJECT_ROOT/.build/tuist-derived/OpenRenderBox/OpenRenderBox.xcodeproj")"
  299. ;;
  300. SymbolLocator)
  301. echo "-project" "$PROJECT_ROOT/.build/tuist-derived/SymbolLocator/SymbolLocator.xcodeproj"
  302. ;;
  303. *)
  304. echo "Error: No Xcode project mapping for $scheme." >&2
  305. exit 1
  306. ;;
  307. esac
  308. }
  309. compute_source_backend_enabled() {
  310. [ "${OPENSWIFTUI_OPENATTRIBUTESHIMS_COMPUTE:-0}" = "1" ] &&
  311. [ "${OPENSWIFTUI_OPENATTRIBUTESHIMS_COMPUTE_BINARY:-0}" != "1" ]
  312. }
  313. compute_project_path() {
  314. first_existing_project "$PROJECT_ROOT/../Compute/Compute.xcodeproj" "$PROJECT_ROOT/.build/tuist-derived/Compute/Compute.xcodeproj"
  315. }
  316. compute_archive_path() {
  317. local sdk="$1"
  318. echo "$PROJECT_BUILD_DIR/Compute-$sdk.xcarchive"
  319. }
  320. compute_framework_search_path() {
  321. local sdk="$1"
  322. dirname "$(framework_path "$(compute_archive_path "$sdk")" "Compute")"
  323. }
  324. prebuild_compute_framework() {
  325. local sdk="$1"
  326. local destination="$2"
  327. local archs="$3"
  328. local archive_path
  329. archive_path="$(compute_archive_path "$sdk")"
  330. local project_path
  331. project_path="$(compute_project_path)"
  332. if [ ! -d "$project_path" ]; then
  333. echo "Error: Expected Tuist to generate $project_path."
  334. exit 1
  335. fi
  336. rm -rf "$archive_path"
  337. local xcodebuild_args=(
  338. archive
  339. -project "$project_path"
  340. -scheme "Compute"
  341. -configuration Release
  342. -archivePath "$archive_path"
  343. -sdk "$sdk"
  344. -destination "$destination"
  345. -derivedDataPath "$DERIVED_DATA_PATH"
  346. -skipPackagePluginValidation
  347. -skipMacroValidation
  348. INSTALL_PATH=Library/Frameworks
  349. SKIP_INSTALL=NO
  350. BUILD_LIBRARY_FOR_DISTRIBUTION=YES
  351. SWIFT_EMIT_MODULE_INTERFACE=YES
  352. ENABLE_USER_SCRIPT_SANDBOXING=NO
  353. )
  354. if [ -n "$archs" ]; then
  355. xcodebuild_args+=("ARCHS=${archs//,/ }")
  356. fi
  357. xcodebuild "${xcodebuild_args[@]}"
  358. local framework
  359. framework="$(framework_path "$archive_path" "Compute")"
  360. if [ ! -d "$framework" ]; then
  361. echo "Error: Archive did not contain $framework."
  362. exit 1
  363. fi
  364. }
  365. build_framework() {
  366. local sdk="$1"
  367. local destination="$2"
  368. local scheme="$3"
  369. local archs="$4"
  370. local archive_path="$PROJECT_BUILD_DIR/$scheme-$sdk.xcarchive"
  371. local project_args
  372. read -r -a project_args <<<"$(project_args_for_scheme "$scheme")"
  373. local framework_search_paths=()
  374. if [ "$scheme" = "OpenAttributeGraphShims" ] && compute_source_backend_enabled; then
  375. prebuild_compute_framework "$sdk" "$destination" "$archs"
  376. framework_search_paths+=("$(compute_framework_search_path "$sdk")")
  377. fi
  378. rm -rf "$archive_path"
  379. local xcodebuild_args=(
  380. archive
  381. "${project_args[@]}"
  382. -scheme "$scheme"
  383. -configuration Release
  384. -archivePath "$archive_path"
  385. -sdk "$sdk"
  386. -destination "$destination"
  387. -derivedDataPath "$DERIVED_DATA_PATH"
  388. -skipPackagePluginValidation
  389. -skipMacroValidation
  390. INSTALL_PATH=Library/Frameworks
  391. SKIP_INSTALL=NO
  392. BUILD_LIBRARY_FOR_DISTRIBUTION=YES
  393. SWIFT_EMIT_MODULE_INTERFACE=YES
  394. "SWIFT_ACTIVE_COMPILATION_CONDITIONS=\$(inherited) OPENSWIFTUI_XCFRAMEWORK_BUILD"
  395. ENABLE_USER_SCRIPT_SANDBOXING=NO
  396. )
  397. if [ -n "$archs" ]; then
  398. xcodebuild_args+=("ARCHS=${archs//,/ }")
  399. fi
  400. if [ ${#framework_search_paths[@]} -gt 0 ]; then
  401. xcodebuild_args+=("FRAMEWORK_SEARCH_PATHS=${framework_search_paths[*]} \$(inherited)")
  402. fi
  403. xcodebuild "${xcodebuild_args[@]}"
  404. local framework
  405. framework="$(framework_path "$archive_path" "$scheme")"
  406. if [ ! -d "$framework" ]; then
  407. echo "Error: Archive did not contain $framework."
  408. exit 1
  409. fi
  410. local modules_path
  411. modules_path="$(framework_modules_path "$framework")"
  412. mkdir -p "$modules_path"
  413. if [ "$sdk" = "macosx" ]; then
  414. rm -rf "$framework/Modules"
  415. ln -s Versions/Current/Modules "$framework/Modules"
  416. fi
  417. strip_release_metadata "$modules_path"
  418. }
  419. create_xcframework() {
  420. local scheme="$1"
  421. local output_path
  422. output_path="$(xcframework_path "$scheme")"
  423. rm -rf "$output_path"
  424. local create_args=()
  425. for sdk in "${SDKS[@]}"; do
  426. create_args+=(-framework "$(framework_path "$PROJECT_BUILD_DIR/$scheme-$sdk.xcarchive" "$scheme")")
  427. done
  428. xcodebuild -create-xcframework "${create_args[@]}" -output "$output_path"
  429. }
  430. copy_debug_symbols() {
  431. local scheme="$1"
  432. if [ "$DEBUG_MODE" = false ]; then
  433. return
  434. fi
  435. local sdk
  436. for sdk in "${SDKS[@]}"; do
  437. local local_dsym_dir=""
  438. case "$sdk" in
  439. iphonesimulator) local_dsym_dir=$(ls -d "$(xcframework_path "$scheme")"/ios-*simulator 2>/dev/null | head -1) ;;
  440. iphoneos) local_dsym_dir=$(ls -d "$(xcframework_path "$scheme")"/ios-arm64 2>/dev/null | head -1) ;;
  441. macosx) local_dsym_dir=$(ls -d "$(xcframework_path "$scheme")"/macos-* 2>/dev/null | head -1) ;;
  442. esac
  443. if [ -n "$local_dsym_dir" ] && [ -d "$PROJECT_BUILD_DIR/$scheme-$sdk.xcarchive/dSYMs" ]; then
  444. cp -R "$PROJECT_BUILD_DIR/$scheme-$sdk.xcarchive/dSYMs" "$local_dsym_dir/"
  445. fi
  446. done
  447. }
  448. rm -rf "$DERIVED_DATA_PATH"
  449. mkdir -p "$PROJECT_BUILD_DIR"
  450. for scheme in "${FRAMEWORK_NAMES[@]}"; do
  451. echo "Building $scheme..."
  452. for i in "${!SDKS[@]}"; do
  453. build_framework "${SDKS[$i]}" "$(sdk_destination "${SDKS[$i]}")" "$scheme" "${SDK_ARCHS[$i]}"
  454. done
  455. create_xcframework "$scheme"
  456. copy_debug_symbols "$scheme"
  457. echo "Created $(xcframework_path "$scheme")"
  458. done
  459. if [ "$DEBUG_MODE" = false ]; then
  460. echo "Skipping dSYMs. Pass --debug to include them in the XCFrameworks."
  461. else
  462. echo "Copied dSYMs into the XCFrameworks."
  463. fi
  464. echo "Created ${#FRAMEWORK_NAMES[@]} XCFrameworks in $PROJECT_BUILD_DIR."