| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549 |
- #!/bin/bash
- set -e
- : "${OPENSWIFTUI_LIBRARY_TYPE:=dynamic}"
- : "${OPENSWIFTUI_USE_LOCAL_DEPS:=1}"
- : "${OPENSWIFTUI_SWIFTUI_RENDERER:=0}"
- export OPENSWIFTUI_LIBRARY_TYPE
- export OPENSWIFTUI_USE_LOCAL_DEPS
- export OPENSWIFTUI_SWIFTUI_RENDERER
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd -P)"
- PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
- cd "$PROJECT_ROOT"
- PROJECT_BUILD_DIR="${PROJECT_BUILD_DIR:-"${PROJECT_ROOT}/build"}"
- DERIVED_DATA_PATH="$PROJECT_BUILD_DIR/DerivedData"
- XCODEPROJ="$PROJECT_ROOT/OpenSwiftUI.xcodeproj"
- XCODEWORKSPACE="$PROJECT_ROOT/Workspace.xcworkspace"
- # Parse arguments.
- # --sdk and --archs are paired: --sdk <sdk> --archs <arch1,arch2>
- # If --archs is omitted for an SDK, all default architectures are built.
- SDKS=()
- SDK_ARCHS=()
- DEBUG_MODE=false
- RUN_TUIST_INSTALL=true
- EXPLICIT_FRAMEWORK_NAMES=false
- FRAMEWORK_NAMES=()
- DEFAULT_FRAMEWORK_NAMES=(
- "OpenAttributeGraphShims"
- "OpenCoreGraphicsShims"
- "OpenQuartzCoreShims"
- "OpenObservation"
- "OpenRenderBoxShims"
- "OpenSwiftUICore"
- "OpenSwiftUI"
- )
- print_usage() {
- cat <<'USAGE'
- Usage: Scripts/build_xcframework.sh [options] [framework ...]
- Options:
- --sdk <sdk> Build for an SDK. May be passed multiple times.
- --archs <arch1,arch2> Override architectures for the previous --sdk.
- --debug Keep release metadata and copy dSYMs.
- --compute Build OpenAttributeGraphShims with the Compute source backend.
- --skip-tuist-install Skip tuist install.
- --framework <name> Build one framework. May be passed multiple times.
- --help Show this help.
- USAGE
- }
- while [[ $# -gt 0 ]]; do
- case "$1" in
- --help|-h)
- print_usage
- exit 0
- ;;
- --sdk)
- SDKS+=("$2")
- SDK_ARCHS+=("")
- shift 2
- ;;
- --archs)
- if [ ${#SDKS[@]} -gt 0 ]; then
- SDK_ARCHS[$((${#SDK_ARCHS[@]} - 1))]="$2"
- fi
- shift 2
- ;;
- --debug)
- DEBUG_MODE=true
- shift
- ;;
- --compute)
- OPENSWIFTUI_OPENATTRIBUTESHIMS_ATTRIBUTEGRAPH=0
- OPENSWIFTUI_OPENATTRIBUTESHIMS_DANCEUIGRAPH=0
- OPENSWIFTUI_OPENATTRIBUTESHIMS_COMPUTE=1
- OPENSWIFTUI_OPENATTRIBUTESHIMS_COMPUTE_BINARY=0
- export OPENSWIFTUI_OPENATTRIBUTESHIMS_ATTRIBUTEGRAPH
- export OPENSWIFTUI_OPENATTRIBUTESHIMS_DANCEUIGRAPH
- export OPENSWIFTUI_OPENATTRIBUTESHIMS_COMPUTE
- export OPENSWIFTUI_OPENATTRIBUTESHIMS_COMPUTE_BINARY
- shift
- ;;
- --skip-tuist-install)
- RUN_TUIST_INSTALL=false
- shift
- ;;
- --framework)
- EXPLICIT_FRAMEWORK_NAMES=true
- FRAMEWORK_NAMES+=("$2")
- shift 2
- ;;
- *)
- FRAMEWORK_NAMES+=("$1")
- shift
- ;;
- esac
- done
- # By default, build the full binary distribution set. Keep
- # `Scripts/build_xcframework.sh OpenSwiftUI` compatible with the historical CI
- # invocation while switching it to the multi-xcframework distribution.
- if [ ${#FRAMEWORK_NAMES[@]} -eq 0 ] ||
- ([ "$EXPLICIT_FRAMEWORK_NAMES" = false ] &&
- [ ${#FRAMEWORK_NAMES[@]} -eq 1 ] &&
- [ "${FRAMEWORK_NAMES[0]}" = "OpenSwiftUI" ]); then
- FRAMEWORK_NAMES=("${DEFAULT_FRAMEWORK_NAMES[@]}")
- fi
- # Default: macosx and iphonesimulator. Compute builds also include iphoneos.
- # Note: iphoneos SDK support is blocked by an AG issue. See #835.
- if [ ${#SDKS[@]} -eq 0 ]; then
- if [ "${OPENSWIFTUI_OPENATTRIBUTESHIMS_COMPUTE:-0}" = "1" ]; then
- SDKS=("macosx" "iphonesimulator" "iphoneos")
- SDK_ARCHS=("" "" "")
- else
- SDKS=("macosx" "iphonesimulator")
- SDK_ARCHS=("" "")
- fi
- fi
- if [ "${OPENSWIFTUI_SKIP_TUIST_INSTALL:-0}" = "1" ]; then
- RUN_TUIST_INSTALL=false
- fi
- if ! command -v tuist >/dev/null 2>&1; then
- echo "Error: tuist is required to generate $XCODEPROJ."
- exit 1
- fi
- if [ "$RUN_TUIST_INSTALL" = true ]; then
- echo "Installing Tuist dependencies..."
- tuist install
- else
- echo "Skipping tuist install."
- fi
- echo "Generating Xcode project with Tuist..."
- tuist generate --no-open
- if [ ! -d "$XCODEPROJ" ]; then
- echo "Error: Expected Tuist to generate $XCODEPROJ."
- exit 1
- fi
- if [ ! -d "$XCODEWORKSPACE" ]; then
- echo "Error: Expected Tuist to generate $XCODEWORKSPACE."
- exit 1
- fi
- remove_generated_macro_references() {
- local project_path="$1"
- local target_name="$2"
- local macro_name="$3"
- if [ ! -f "$project_path/project.pbxproj" ]; then
- return
- fi
- ruby - "$project_path/project.pbxproj" "$target_name" "$macro_name" <<'RUBY'
- path, target_name, macro_name = ARGV
- text = File.read(path)
- object_pattern = /^ ([A-Za-z0-9]+) \/\* [^*]+ \*\/ = \{\n.*?^ \};\n/m
- dependency_names = {}
- text.scan(object_pattern) do |capture|
- id = Array(capture).first
- block = Regexp.last_match(0)
- next unless block.include?("isa = PBXTargetDependency;")
- dependency_names[id] =
- block[/^ name = ([^;]+);$/, 1] ||
- block[/^ target = [A-Za-z0-9]+ \/\* ([^*]+) \*\//, 1]
- end
- macro_phase_ids = []
- text.scan(object_pattern) do |capture|
- id = Array(capture).first
- block = Regexp.last_match(0)
- next unless block.include?("isa = PBXShellScriptBuildPhase;")
- next unless block.include?(macro_name)
- macro_phase_ids << id
- end
- macro_target_ids = []
- text.scan(object_pattern) do |capture|
- id = Array(capture).first
- block = Regexp.last_match(0)
- next unless block.include?("isa = PBXNativeTarget;")
- next unless block.match?(/^ name = #{Regexp.escape(macro_name)};$/)
- macro_target_ids << id
- end
- target_dependency_ids = []
- text.scan(object_pattern) do |_capture|
- block = Regexp.last_match(0)
- next unless block.include?("isa = PBXNativeTarget;")
- next unless block.match?(/^ name = #{Regexp.escape(target_name)};$/)
- dependencies = block[/^ dependencies = \(\n(.*?)^ \);$/m, 1]
- target_dependency_ids = dependencies.to_s.scan(/^\s+([A-Za-z0-9]+) \/\* PBXTargetDependency \*\/,/).flatten
- break
- end
- dependency_ids_to_remove = target_dependency_ids.select { |id| dependency_names[id] == macro_name }
- # Tuist can leave a standalone macro PBXTargetDependency in derived projects even
- # when the framework target does not list it in `dependencies`. Xcode's automatic
- # scheme discovery can still pull that target into archive builds, so prune it
- # when it is unambiguous.
- if dependency_ids_to_remove.empty?
- matching_ids = dependency_names.select { |_id, name| name == macro_name }.keys
- dependency_ids_to_remove = matching_ids if matching_ids.one?
- end
- changed = false
- dependency_ids_to_remove.each do |id|
- text.gsub!(/^ #{Regexp.escape(id)} \/\* PBXTargetDependency \*\/,\n/, "")
- removed = text.gsub!(/^ #{Regexp.escape(id)} \/\* PBXTargetDependency \*\/ = \{\n.*?^ \};\n/m, "")
- changed ||= !!removed
- end
- macro_phase_ids.each do |id|
- text.gsub!(/^ #{Regexp.escape(id)} \/\* [^*]+ \*\/,\n/, "")
- removed = text.gsub!(/^ #{Regexp.escape(id)} \/\* [^*]+ \*\/ = \{\n.*?^ \};\n/m, "")
- changed ||= !!removed
- end
- macro_target_ids.each do |id|
- removed = text.gsub!(/^ #{Regexp.escape(id)} \/\* #{Regexp.escape(macro_name)} \*\/,\n/, "")
- changed ||= !!removed
- end
- plugin_path = '$BUILD_DIR/Debug$EFFECTIVE_PLATFORM_NAME/' + macro_name + '#' + macro_name
- plugin_path_pattern = Regexp.escape(plugin_path)
- removed_flags = text.gsub!(
- /^(\t+)"-load-plugin-executable",\n\1"#{plugin_path_pattern}",\n/,
- ""
- )
- changed ||= !!removed_flags
- exit unless changed
- File.write(path, text)
- puts "Removed #{macro_name} archive references from #{File.dirname(path)}"
- RUBY
- }
- # The xcframework archive path sets OPENSWIFTUI_XCFRAMEWORK_BUILD, which expands
- # macro usages inline where needed. Avoid forcing generated macro tool targets to
- # archive for simulator SDKs, where Xcode can try to build them for the target
- # platform instead of the host platform.
- remove_generated_macro_references "$PROJECT_ROOT/.build/tuist-derived/OpenObservation/OpenObservation.xcodeproj" "OpenObservation" "OpenObservationMacros"
- remove_generated_macro_references "$PROJECT_ROOT/../OpenObservation/OpenObservation.xcodeproj" "OpenObservation" "OpenObservationMacros"
- remove_generated_macro_references "$XCODEPROJ" "OpenSwiftUICore" "OpenSwiftUIMacros"
- remove_generated_macro_references "$XCODEPROJ" "OpenSwiftUICore" "OpenObservationMacros"
- echo "SDKs: ${SDKS[*]}"
- for i in "${!SDKS[@]}"; do
- if [ -n "${SDK_ARCHS[$i]}" ]; then
- echo " ${SDKS[$i]}: ARCHS=${SDK_ARCHS[$i]}"
- else
- echo " ${SDKS[$i]}: (default archs)"
- fi
- done
- echo "Debug mode: $DEBUG_MODE"
- echo "Frameworks: ${FRAMEWORK_NAMES[*]}"
- sdk_destination() {
- case "$1" in
- iphonesimulator) echo "generic/platform=iOS Simulator" ;;
- iphoneos) echo "generic/platform=iOS" ;;
- macosx) echo "generic/platform=macOS" ;;
- *) echo "generic/platform=$1" ;;
- esac
- }
- framework_path() {
- local archive_path="$1"
- local scheme="$2"
- echo "$archive_path/Products/Library/Frameworks/$scheme.framework"
- }
- xcframework_path() {
- local scheme="$1"
- echo "$PROJECT_BUILD_DIR/$scheme.xcframework"
- }
- framework_modules_path() {
- local framework="$1"
- if [ -d "$framework/Versions" ]; then
- echo "$framework/Versions/Current/Modules"
- else
- echo "$framework/Modules"
- fi
- }
- strip_release_metadata() {
- local modules_path="$1"
- if [ "$DEBUG_MODE" = false ]; then
- find "$modules_path" -name "*.abi.json" -delete
- find "$modules_path" -name "*.package.swiftinterface" -delete
- find "$modules_path" -name "*.private.swiftinterface" -delete
- fi
- }
- first_existing_project() {
- local candidate
- for candidate in "$@"; do
- if [ -d "$candidate" ]; then
- echo "$candidate"
- return
- fi
- done
- echo "$1"
- }
- project_args_for_scheme() {
- local scheme="$1"
- case "$scheme" in
- COpenSwiftUI|OpenSwiftUI|OpenSwiftUICore|OpenSwiftUI_SPI|OpenSwiftUISymbolDualTestsSupport)
- echo "-workspace" "$XCODEWORKSPACE"
- ;;
- _AttributeGraphDeviceSwiftShims)
- echo "-project" "$(first_existing_project "$PROJECT_ROOT/../DarwinPrivateFrameworks/DarwinPrivateFrameworks.xcodeproj" "$PROJECT_ROOT/.build/tuist-derived/DarwinPrivateFrameworks/DarwinPrivateFrameworks.xcodeproj")"
- ;;
- OpenAttributeGraphShims)
- echo "-project" "$(first_existing_project "$PROJECT_ROOT/../OpenAttributeGraph/OpenAttributeGraph.xcodeproj" "$PROJECT_ROOT/.build/tuist-derived/OpenAttributeGraph/OpenAttributeGraph.xcodeproj")"
- ;;
- OpenCoreGraphics|OpenCoreGraphicsShims|OpenQuartzCore|OpenQuartzCoreShims)
- echo "-project" "$(first_existing_project "$PROJECT_ROOT/../OpenCoreGraphics/OpenCoreGraphics.xcodeproj" "$PROJECT_ROOT/.build/tuist-derived/OpenCoreGraphics/OpenCoreGraphics.xcodeproj")"
- ;;
- OpenObservation|OpenObservationCxx)
- echo "-project" "$(first_existing_project "$PROJECT_ROOT/../OpenObservation/OpenObservation.xcodeproj" "$PROJECT_ROOT/.build/tuist-derived/OpenObservation/OpenObservation.xcodeproj")"
- ;;
- OpenRenderBoxShims)
- echo "-project" "$(first_existing_project "$PROJECT_ROOT/../OpenRenderBox/OpenRenderBox.xcodeproj" "$PROJECT_ROOT/.build/tuist-derived/OpenRenderBox/OpenRenderBox.xcodeproj")"
- ;;
- SymbolLocator)
- echo "-project" "$PROJECT_ROOT/.build/tuist-derived/SymbolLocator/SymbolLocator.xcodeproj"
- ;;
- *)
- echo "Error: No Xcode project mapping for $scheme." >&2
- exit 1
- ;;
- esac
- }
- compute_source_backend_enabled() {
- [ "${OPENSWIFTUI_OPENATTRIBUTESHIMS_COMPUTE:-0}" = "1" ] &&
- [ "${OPENSWIFTUI_OPENATTRIBUTESHIMS_COMPUTE_BINARY:-0}" != "1" ]
- }
- compute_project_path() {
- first_existing_project "$PROJECT_ROOT/../Compute/Compute.xcodeproj" "$PROJECT_ROOT/.build/tuist-derived/Compute/Compute.xcodeproj"
- }
- compute_archive_path() {
- local sdk="$1"
- echo "$PROJECT_BUILD_DIR/Compute-$sdk.xcarchive"
- }
- compute_framework_search_path() {
- local sdk="$1"
- dirname "$(framework_path "$(compute_archive_path "$sdk")" "Compute")"
- }
- prebuild_compute_framework() {
- local sdk="$1"
- local destination="$2"
- local archs="$3"
- local archive_path
- archive_path="$(compute_archive_path "$sdk")"
- local project_path
- project_path="$(compute_project_path)"
- if [ ! -d "$project_path" ]; then
- echo "Error: Expected Tuist to generate $project_path."
- exit 1
- fi
- rm -rf "$archive_path"
- local xcodebuild_args=(
- archive
- -project "$project_path"
- -scheme "Compute"
- -configuration Release
- -archivePath "$archive_path"
- -sdk "$sdk"
- -destination "$destination"
- -derivedDataPath "$DERIVED_DATA_PATH"
- -skipPackagePluginValidation
- -skipMacroValidation
- INSTALL_PATH=Library/Frameworks
- SKIP_INSTALL=NO
- BUILD_LIBRARY_FOR_DISTRIBUTION=YES
- SWIFT_EMIT_MODULE_INTERFACE=YES
- ENABLE_USER_SCRIPT_SANDBOXING=NO
- )
- if [ -n "$archs" ]; then
- xcodebuild_args+=("ARCHS=${archs//,/ }")
- fi
- xcodebuild "${xcodebuild_args[@]}"
- local framework
- framework="$(framework_path "$archive_path" "Compute")"
- if [ ! -d "$framework" ]; then
- echo "Error: Archive did not contain $framework."
- exit 1
- fi
- }
- build_framework() {
- local sdk="$1"
- local destination="$2"
- local scheme="$3"
- local archs="$4"
- local archive_path="$PROJECT_BUILD_DIR/$scheme-$sdk.xcarchive"
- local project_args
- read -r -a project_args <<<"$(project_args_for_scheme "$scheme")"
- local framework_search_paths=()
- if [ "$scheme" = "OpenAttributeGraphShims" ] && compute_source_backend_enabled; then
- prebuild_compute_framework "$sdk" "$destination" "$archs"
- framework_search_paths+=("$(compute_framework_search_path "$sdk")")
- fi
- rm -rf "$archive_path"
- local xcodebuild_args=(
- archive
- "${project_args[@]}"
- -scheme "$scheme"
- -configuration Release
- -archivePath "$archive_path"
- -sdk "$sdk"
- -destination "$destination"
- -derivedDataPath "$DERIVED_DATA_PATH"
- -skipPackagePluginValidation
- -skipMacroValidation
- INSTALL_PATH=Library/Frameworks
- SKIP_INSTALL=NO
- BUILD_LIBRARY_FOR_DISTRIBUTION=YES
- SWIFT_EMIT_MODULE_INTERFACE=YES
- "SWIFT_ACTIVE_COMPILATION_CONDITIONS=\$(inherited) OPENSWIFTUI_XCFRAMEWORK_BUILD"
- ENABLE_USER_SCRIPT_SANDBOXING=NO
- )
- if [ -n "$archs" ]; then
- xcodebuild_args+=("ARCHS=${archs//,/ }")
- fi
- if [ ${#framework_search_paths[@]} -gt 0 ]; then
- xcodebuild_args+=("FRAMEWORK_SEARCH_PATHS=${framework_search_paths[*]} \$(inherited)")
- fi
- xcodebuild "${xcodebuild_args[@]}"
- local framework
- framework="$(framework_path "$archive_path" "$scheme")"
- if [ ! -d "$framework" ]; then
- echo "Error: Archive did not contain $framework."
- exit 1
- fi
- local modules_path
- modules_path="$(framework_modules_path "$framework")"
- mkdir -p "$modules_path"
- if [ "$sdk" = "macosx" ]; then
- rm -rf "$framework/Modules"
- ln -s Versions/Current/Modules "$framework/Modules"
- fi
- strip_release_metadata "$modules_path"
- }
- create_xcframework() {
- local scheme="$1"
- local output_path
- output_path="$(xcframework_path "$scheme")"
- rm -rf "$output_path"
- local create_args=()
- for sdk in "${SDKS[@]}"; do
- create_args+=(-framework "$(framework_path "$PROJECT_BUILD_DIR/$scheme-$sdk.xcarchive" "$scheme")")
- done
- xcodebuild -create-xcframework "${create_args[@]}" -output "$output_path"
- }
- copy_debug_symbols() {
- local scheme="$1"
- if [ "$DEBUG_MODE" = false ]; then
- return
- fi
- local sdk
- for sdk in "${SDKS[@]}"; do
- local local_dsym_dir=""
- case "$sdk" in
- iphonesimulator) local_dsym_dir=$(ls -d "$(xcframework_path "$scheme")"/ios-*simulator 2>/dev/null | head -1) ;;
- iphoneos) local_dsym_dir=$(ls -d "$(xcframework_path "$scheme")"/ios-arm64 2>/dev/null | head -1) ;;
- macosx) local_dsym_dir=$(ls -d "$(xcframework_path "$scheme")"/macos-* 2>/dev/null | head -1) ;;
- esac
- if [ -n "$local_dsym_dir" ] && [ -d "$PROJECT_BUILD_DIR/$scheme-$sdk.xcarchive/dSYMs" ]; then
- cp -R "$PROJECT_BUILD_DIR/$scheme-$sdk.xcarchive/dSYMs" "$local_dsym_dir/"
- fi
- done
- }
- rm -rf "$DERIVED_DATA_PATH"
- mkdir -p "$PROJECT_BUILD_DIR"
- for scheme in "${FRAMEWORK_NAMES[@]}"; do
- echo "Building $scheme..."
- for i in "${!SDKS[@]}"; do
- build_framework "${SDKS[$i]}" "$(sdk_destination "${SDKS[$i]}")" "$scheme" "${SDK_ARCHS[$i]}"
- done
- create_xcframework "$scheme"
- copy_debug_symbols "$scheme"
- echo "Created $(xcframework_path "$scheme")"
- done
- if [ "$DEBUG_MODE" = false ]; then
- echo "Skipping dSYMs. Pass --debug to include them in the XCFrameworks."
- else
- echo "Copied dSYMs into the XCFrameworks."
- fi
- echo "Created ${#FRAMEWORK_NAMES[@]} XCFrameworks in $PROJECT_BUILD_DIR."
|