build_xcframework.sh 17 KB

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