this repo has no description
at main 885 lines 31 kB view raw
1#!/bin/bash 2# Builds the required IDB Frameworks for the AXe project. 3 4set -e 5set -o pipefail 6 7# Environment and Configuration 8IDB_CHECKOUT_DIR="${IDB_CHECKOUT_DIR:-./idb_checkout}" 9IDB_GIT_REF="${IDB_GIT_REF:-76639e4d0e1741adf391cab36f19fbc59378153e}" 10BUILD_OUTPUT_DIR="${BUILD_OUTPUT_DIR:-./build_products}" 11DERIVED_DATA_PATH="${DERIVED_DATA_PATH:-./build_derived_data}" 12BUILD_XCFRAMEWORK_DIR="${BUILD_XCFRAMEWORK_DIR:-${BUILD_OUTPUT_DIR}/XCFrameworks}" 13FBSIMCONTROL_PROJECT="${IDB_CHECKOUT_DIR}/FBSimulatorControl.xcodeproj" 14TEMP_DIR="${TEMP_DIR:-$(mktemp -d)}" 15 16FRAMEWORK_SDK="macosx" 17FRAMEWORK_CONFIGURATION="Release" 18 19# Notarization Configuration 20NOTARIZATION_API_KEY_PATH="${NOTARIZATION_API_KEY_PATH:-./keys/AuthKey_8TJYVXVDQ6.p8}" 21NOTARIZATION_KEY_ID="${NOTARIZATION_KEY_ID:-8TJYVXVDQ6}" 22NOTARIZATION_ISSUER_ID="${NOTARIZATION_ISSUER_ID:-69a6de8e-e388-47e3-e053-5b8c7c11a4d1}" 23 24# --- Helper Functions --- 25 26# Temporarily disable xcpretty to see actual errors in CI 27# if hash xcpretty 2>/dev/null; then 28# HAS_XCPRETTY=true 29# fi 30 31# Function to print a section header with emoji 32function print_section() { 33 local emoji="$1" 34 local title="$2" 35 echo "" 36 echo "" 37 echo "${emoji} ${title}" 38 echo "$(printf '·%.0s' {1..60})" 39} 40 41# Function to print a subsection header 42function print_subsection() { 43 local emoji="$1" 44 local title="$2" 45 echo "" 46 echo "${emoji} ${title}" 47} 48 49# Function to print success message 50function print_success() { 51 local message="$1" 52 echo "${message}" 53} 54 55# Function to print info message 56function print_info() { 57 local message="$1" 58 echo "ℹ️ ${message}" 59} 60 61# Function to print warning message 62function print_warning() { 63 local message="$1" 64 echo "⚠️ ${message}" 65} 66 67# Function to invoke xcodebuild, optionally with xcpretty 68function invoke_xcodebuild() { 69 local arguments=$@ 70 print_info "Executing: xcodebuild ${arguments[*]}" 71 72 local exit_code 73 if [[ -n $HAS_XCPRETTY ]]; then 74 NSUnbufferedIO=YES xcodebuild $arguments | xcpretty -c 75 exit_code=${PIPESTATUS[0]} 76 else 77 xcodebuild $arguments 2>&1 78 exit_code=$? 79 fi 80 81 return $exit_code 82} 83 84function clone_idb_repo() { 85 if [ ! -d $IDB_CHECKOUT_DIR ]; then 86 print_info "Creating $IDB_DIRECTORY directory and cloning idb repository..." 87 git clone https://github.com/facebook/idb.git $IDB_CHECKOUT_DIR 88 (cd $IDB_CHECKOUT_DIR && git checkout "$IDB_GIT_REF") 89 print_success "idb repository cloned at $IDB_GIT_REF." 90 else 91 print_info "Updating idb repository to $IDB_GIT_REF..." 92 (cd $IDB_CHECKOUT_DIR && git fetch --all --tags --prune && git reset --hard "$IDB_GIT_REF") 93 print_success "idb repository updated to $IDB_GIT_REF." 94 fi 95} 96 97# Function to build a single framework 98# $1: Scheme name 99# $2: Project file path 100# $3: Base output directory (for .framework and .xcframework) 101function framework_build() { 102 local scheme_name="$1" 103 local project_file="$2" 104 local output_base_dir="$3" 105 106 print_subsection "🔨" "Building framework: ${scheme_name}" 107 print_info "Project: ${project_file}" 108 109 invoke_xcodebuild \ 110 -project "${project_file}" \ 111 -scheme "${scheme_name}" \ 112 -sdk "${FRAMEWORK_SDK}" \ 113 -destination "generic/platform=macOS" \ 114 -configuration "${FRAMEWORK_CONFIGURATION}" \ 115 -derivedDataPath "${DERIVED_DATA_PATH}" \ 116 build \ 117 SKIP_INSTALL=NO \ 118 ONLY_ACTIVE_ARCH=NO \ 119 BUILD_LIBRARY_FOR_DISTRIBUTION=YES \ 120 GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS=NO \ 121 CLANG_WARN_DOCUMENTATION_COMMENTS=NO \ 122 GCC_TREAT_WARNINGS_AS_ERRORS=NO \ 123 SWIFT_TREAT_WARNINGS_AS_ERRORS=NO 124 local build_exit_code=$? 125 126 if [ $build_exit_code -eq 0 ]; then 127 print_success "Framework ${scheme_name} built successfully!" 128 else 129 echo "❌ Error: Framework ${scheme_name} build failed with exit code ${build_exit_code}" 130 exit $build_exit_code 131 fi 132} 133 134# Function to install a single framework to Frameworks/ 135# $1: Scheme name (used to find the .framework in derived data) 136# $2: Base output directory 137function install_framework() { 138 local scheme_name="$1" 139 local output_base_dir="$2" 140 local built_framework_path="${DERIVED_DATA_PATH}/Build/Products/${FRAMEWORK_CONFIGURATION}/${scheme_name}.framework" 141 local final_framework_install_dir="${output_base_dir}/Frameworks" 142 143 print_info "Installing framework ${scheme_name}.framework to ${final_framework_install_dir}..." 144 if [[ ! -d "${built_framework_path}" ]]; then 145 echo "❌ Error: Built framework not found at ${built_framework_path} for installation." 146 exit 1 147 fi 148 149 mkdir -p "${final_framework_install_dir}" 150 print_info "Copying ${built_framework_path} to ${final_framework_install_dir}/" 151 cp -R "${built_framework_path}" "${final_framework_install_dir}/" 152 print_success "Framework ${scheme_name}.framework installed to ${final_framework_install_dir}/" 153} 154 155# Function to create a single XCFramework 156# $1: Scheme name 157# $2: Base output directory (where XCFrameworks/ subdirectory will be created) 158function create_xcframework() { 159 local scheme_name="$1" 160 local output_base_dir="$2" 161 local signed_framework_path="${output_base_dir}/Frameworks/${scheme_name}.framework" 162 local final_xcframework_output_dir="${output_base_dir}/XCFrameworks" 163 local xcframework_path="${final_xcframework_output_dir}/${scheme_name}.xcframework" 164 165 print_subsection "📦" "Creating XCFramework for ${scheme_name}" 166 if [[ ! -d "${signed_framework_path}" ]]; then 167 echo "❌ Error: Signed framework not found at ${signed_framework_path} for XCFramework creation." 168 exit 1 169 fi 170 171 mkdir -p "${final_xcframework_output_dir}" 172 rm -rf "${xcframework_path}" 173 174 print_info "Packaging ${signed_framework_path} into ${xcframework_path}" 175 invoke_xcodebuild \ 176 -create-xcframework \ 177 -framework "${signed_framework_path}" \ 178 -output "${xcframework_path}" 179 local xcframework_exit_code=$? 180 181 if [ $xcframework_exit_code -eq 0 ]; then 182 print_success "XCFramework ${scheme_name}.xcframework created at ${xcframework_path}" 183 else 184 echo "❌ Error: XCFramework creation for ${scheme_name} failed with exit code ${xcframework_exit_code}" 185 exit $xcframework_exit_code 186 fi 187} 188 189# Function to strip a framework of nested frameworks 190# $1: Base output directory 191# $2: Framework path 192function strip_framework() { 193 local output_base_dir="$1" 194 local framework_path="${output_base_dir}/Frameworks/${2}" 195 196 if [ -d "$framework_path" ]; then 197 print_info "Stripping Framework $framework_path" 198 rm -r "$framework_path" 199 fi 200} 201 202# Function to resign a framework with Developer ID 203# $1: Base output directory 204# $2: Framework name (e.g., "FBSimulatorControl.framework") 205function resign_framework() { 206 local output_base_dir="$1" 207 local framework_name="$2" 208 local framework_path="${output_base_dir}/Frameworks/${framework_name}" 209 210 if [ -d "$framework_path" ]; then 211 print_info "Resigning framework: ${framework_name}" 212 213 # First, sign all dynamic libraries and binaries inside the framework 214 print_info "Signing embedded binaries in ${framework_name}..." 215 216 # Find and sign all .dylib files recursively 217 find "$framework_path" -name "*.dylib" -type f | while read -r dylib_path; do 218 print_info " Signing dylib: $(basename "$dylib_path")" 219 codesign --force \ 220 --sign "Apple Distribution: Wei Wang (A4YJ9MRZ66)" \ 221 --options runtime \ 222 --timestamp \ 223 --verbose \ 224 "$dylib_path" 225 226 if [ $? -ne 0 ]; then 227 echo "❌ Error: Failed to sign dylib: $dylib_path" 228 exit 1 229 fi 230 done 231 232 # Remove any existing signature from the main framework binary first 233 print_info "Removing existing signature from ${framework_name}..." 234 codesign --remove-signature "$framework_path" 2>/dev/null || true 235 236 # Sign the main framework bundle with specific notarization-compatible options 237 print_info "Signing main framework bundle: ${framework_name}" 238 codesign --force \ 239 --sign "Apple Distribution: Wei Wang (A4YJ9MRZ66)" \ 240 --options runtime \ 241 --entitlements entitlements.plist \ 242 --timestamp \ 243 --verbose \ 244 "$framework_path" 245 246 if [ $? -eq 0 ]; then 247 print_success "Framework ${framework_name} resigned successfully" 248 249 # Verify the signature with strictest verification 250 print_info "Performing strict verification for ${framework_name}..." 251 codesign -vvv --strict "$framework_path" 252 253 if [ $? -eq 0 ]; then 254 print_success "Signature verification passed for ${framework_name}" 255 256 # Display signature details 257 print_info "Signature details for ${framework_name}:" 258 codesign -dv "$framework_path" 2>&1 | grep -E "(Identifier|TeamIdentifier|Authority|Timestamp)" || true 259 else 260 echo "❌ Error: Signature verification failed for ${framework_name}" 261 exit 1 262 fi 263 else 264 echo "❌ Error: Failed to resign framework ${framework_name}" 265 exit 1 266 fi 267 else 268 print_warning "Framework not found: $framework_path" 269 fi 270} 271 272# Function to resign an XCFramework with Developer ID 273# $1: Base output directory 274# $2: XCFramework name (e.g., "FBSimulatorControl.xcframework") 275function resign_xcframework() { 276 local output_base_dir="$1" 277 local xcframework_name="$2" 278 local xcframework_path="${output_base_dir}/XCFrameworks/${xcframework_name}" 279 280 if [ -d "$xcframework_path" ]; then 281 print_info "Resigning XCFramework: ${xcframework_name}" 282 283 # Sign XCFramework with Developer ID and runtime hardening 284 codesign --force \ 285 --sign "Apple Distribution: Wei Wang (A4YJ9MRZ66)" \ 286 --options runtime \ 287 --deep \ 288 --timestamp \ 289 "$xcframework_path" 290 291 if [ $? -eq 0 ]; then 292 print_success "XCFramework ${xcframework_name} resigned successfully" 293 294 # Verify the signature with strictest verification and deep checking 295 print_info "Performing strict verification for XCFramework ${xcframework_name}..." 296 codesign -vvv --deep "$xcframework_path" 297 298 if [ $? -eq 0 ]; then 299 print_success "XCFramework signature verification passed for ${xcframework_name}" 300 301 # Display signature details 302 print_info "XCFramework signature details for ${xcframework_name}:" 303 codesign -dv --deep "$xcframework_path" 2>&1 | grep -E "(Identifier|TeamIdentifier|Authority)" || true 304 else 305 echo "❌ Error: XCFramework signature verification failed for ${xcframework_name}" 306 exit 1 307 fi 308 else 309 echo "❌ Error: Failed to resign XCFramework ${xcframework_name}" 310 exit 1 311 fi 312 else 313 print_warning "XCFramework not found: $xcframework_path" 314 fi 315} 316 317# Function to build the AXe executable using Swift Package Manager 318# $1: Base output directory 319function build_axe_executable() { 320 local output_base_dir="$1" 321 local build_config="release" 322 local executable_source=".build/arm64-apple-macosx/${build_config}/axe" 323 local executable_dest="${output_base_dir}/axe" 324 325 print_subsection "⚡" "Building AXe executable" 326 print_info "Using Swift Package Manager to build AXe..." 327 328 # Clean any existing build products to ensure fresh build 329 print_info "Cleaning previous build products..." 330 swift package clean 331 332 # Build using Swift Package Manager (rely on environment variables for cache control) 333 swift build --configuration ${build_config} 334 local build_exit_code=$? 335 336 if [ $build_exit_code -eq 0 ]; then 337 print_success "AXe executable built successfully!" 338 339 # Copy executable to build products directory 340 print_info "Installing executable to ${executable_dest}" 341 cp "${executable_source}" "${executable_dest}" 342 print_success "AXe executable installed to ${executable_dest}" 343 344 # Configure rpath for organized framework loading 345 print_info "Configuring executable rpath for organized framework loading..." 346 347 # Remove any existing rpaths first 348 install_name_tool -delete_rpath "@executable_path/Frameworks" "${executable_dest}" 2>/dev/null || true 349 install_name_tool -delete_rpath "@loader_path/Frameworks" "${executable_dest}" 2>/dev/null || true 350 351 # Add primary rpath: look for frameworks in Frameworks/ subdirectory relative to executable 352 install_name_tool -add_rpath "@executable_path/Frameworks" "${executable_dest}" 353 print_success "Added rpath: @executable_path/Frameworks" 354 355 # Add fallback rpath: look for frameworks in Frameworks/ relative to current library 356 install_name_tool -add_rpath "@loader_path/Frameworks" "${executable_dest}" 357 print_success "Added rpath: @loader_path/Frameworks" 358 359 # Verify rpath configuration 360 print_info "Verifying rpath configuration..." 361 local rpath_output=$(otool -l "${executable_dest}" | grep -A2 LC_RPATH | grep path | awk '{print $2}') 362 if [[ -n "$rpath_output" ]]; then 363 print_success "Executable rpath configuration verified:" 364 echo "$rpath_output" | while read -r path; do 365 print_info "${path}" 366 done 367 else 368 print_warning "No rpath entries found in executable" 369 fi 370 371 print_success "Executable rpath configured for organized framework deployment" 372 else 373 echo "❌ Error: AXe executable build failed with exit code ${build_exit_code}" 374 exit $build_exit_code 375 fi 376} 377 378# Function to sign the AXe executable with Developer ID 379# $1: Base output directory 380function sign_axe_executable() { 381 local output_base_dir="$1" 382 local executable_path="${output_base_dir}/axe" 383 384 if [ -f "$executable_path" ]; then 385 print_info "Signing AXe executable: ${executable_path}" 386 387 # Sign with Developer ID and runtime hardening 388 codesign --force \ 389 --sign "Apple Distribution: Wei Wang (A4YJ9MRZ66)" \ 390 --options runtime \ 391 --entitlements entitlements.plist \ 392 --timestamp \ 393 "$executable_path" 394 395 if [ $? -eq 0 ]; then 396 print_success "AXe executable signed successfully" 397 398 # Verify the signature with strictest verification 399 print_info "Performing strict verification for AXe executable..." 400 codesign -vvv "$executable_path" 401 402 if [ $? -eq 0 ]; then 403 print_success "AXe executable signature verification passed" 404 405 # Display signature details 406 print_info "AXe executable signature details:" 407 codesign -dv "$executable_path" 2>&1 | grep -E "(Identifier|TeamIdentifier|Authority)" || true 408 else 409 echo "❌ Error: AXe executable signature verification failed" 410 exit 1 411 fi 412 else 413 echo "❌ Error: Failed to sign AXe executable" 414 exit 1 415 fi 416 else 417 print_warning "AXe executable not found: $executable_path" 418 fi 419} 420 421# Function to create a package for notarization 422# $1: Base output directory 423function package_for_notarization() { 424 local output_base_dir="$1" 425 local package_name="AXe-$(date +%Y%m%d-%H%M%S)" 426 local package_dir="${output_base_dir}/${package_name}" 427 local package_zip="${output_base_dir}/${package_name}.zip" 428 429 print_subsection "📦" "Creating notarization package" >&2 430 print_info "Package name: ${package_name}" >&2 431 432 # Create temporary package directory 433 rm -rf "${package_dir}" "${package_zip}" 434 mkdir -p "${package_dir}" 435 436 # Copy executable to package directory 437 print_info "Copying executable to package..." >&2 438 cp "${output_base_dir}/axe" "${package_dir}/" 439 440 # Create zip package (redirect zip output to stderr) 441 print_info "Creating zip package: ${package_zip}" >&2 442 (cd "${output_base_dir}" && zip -r "${package_name}.zip" "${package_name}/") >&2 443 444 # Clean up temporary directory 445 rm -rf "${package_dir}" 446 447 if [ -f "${package_zip}" ]; then 448 print_success "Notarization package created: ${package_zip}" >&2 449 # Store the clean absolute path 450 local clean_path="$(cd "$(dirname "${package_zip}")" && pwd)/$(basename "${package_zip}")" 451 # Only echo the path to stdout for capture 452 echo "${clean_path}" 453 else 454 echo "❌ Error: Failed to create notarization package" >&2 455 exit 1 456 fi 457} 458 459# Function to submit package for notarization 460# $1: Package zip path 461function notarize_package() { 462 local package_zip="$1" 463 464 print_subsection "🍎" "Submitting for Apple notarization" 465 466 # Check if API key exists 467 if [ ! -f "${NOTARIZATION_API_KEY_PATH}" ]; then 468 echo "❌ Error: Notarization API key not found at ${NOTARIZATION_API_KEY_PATH}" 469 print_info "Please ensure the API key file exists or set NOTARIZATION_API_KEY_PATH environment variable" 470 exit 1 471 fi 472 473 print_info "API Key: ${NOTARIZATION_API_KEY_PATH}" 474 print_info "Key ID: ${NOTARIZATION_KEY_ID}" 475 print_info "Issuer ID: ${NOTARIZATION_ISSUER_ID}" 476 print_info "Package: ${package_zip}" 477 print_info "Temporary directory: ${TEMP_DIR}" 478 479 # Submit for notarization 480 print_info "Submitting package for notarization..." 481 local submit_output=$(xcrun notarytool submit "${package_zip}" \ 482 --key "${NOTARIZATION_API_KEY_PATH}" \ 483 --key-id "${NOTARIZATION_KEY_ID}" \ 484 --issuer "${NOTARIZATION_ISSUER_ID}" \ 485 --wait 2>&1) 486 local submit_exit_code=$? 487 488 echo "${submit_output}" 489 490 if [ $submit_exit_code -eq 0 ] && echo "${submit_output}" | grep -q "status: Accepted"; then 491 # Extract submission ID from output 492 local submission_id=$(echo "${submit_output}" | grep "id:" | head -1 | awk '{print $2}') 493 print_success "Notarization completed successfully!" 494 print_info "Submission ID: ${submission_id}" 495 496 # Extract notarized executable from package and replace original 497 print_info "Extracting notarized executable to replace original..." 498 local temp_extract_dir="${BUILD_OUTPUT_DIR}/temp_notarized" 499 rm -rf "${temp_extract_dir}" 500 mkdir -p "${temp_extract_dir}" 501 502 # Extract the notarized package 503 unzip -q "${package_zip}" -d "${temp_extract_dir}" 504 505 # Find the extracted executable 506 local extracted_executable=$(find "${temp_extract_dir}" -name "axe" -type f | head -1) 507 508 if [ -f "${extracted_executable}" ]; then 509 # Replace the original executable with the notarized one 510 cp "${extracted_executable}" "${BUILD_OUTPUT_DIR}/axe" 511 print_success "Original executable replaced with notarized version" 512 513 # Verify notarization status using spctl 514 print_info "Verifying notarization with spctl assessment..." 515 spctl -a -v "${BUILD_OUTPUT_DIR}/axe" 2>&1 | grep -q "accepted" || { 516 print_info "Note: spctl shows 'not an app' for command-line tools - this is expected" 517 print_info "Notarized command-line tools are validated differently by macOS" 518 } 519 520 # Check if the executable has the notarization signature 521 print_info "Checking code signature details..." 522 local sig_info=$(codesign -dv "${BUILD_OUTPUT_DIR}/axe" 2>&1) 523 if echo "$sig_info" | grep -q "runtime"; then 524 print_success "Executable has runtime hardening enabled (required for notarization)" 525 else 526 print_warning "Runtime hardening not detected in signature" 527 fi 528 529 print_success "Notarized executable is ready for distribution" 530 531 # Ensure Frameworks directory exists for final package 532 print_info "Preparing Frameworks directory for final package..." 533 if [ ! -d "${BUILD_OUTPUT_DIR}/Frameworks" ]; then 534 print_info "Frameworks directory not found, recreating from XCFrameworks..." 535 mkdir -p "${BUILD_OUTPUT_DIR}/Frameworks" 536 537 # Extract frameworks from XCFrameworks for deployment 538 for xcframework in "${BUILD_OUTPUT_DIR}/XCFrameworks"/*.xcframework; do 539 if [ -d "$xcframework" ]; then 540 local framework_name=$(basename "$xcframework" .xcframework) 541 print_info "Extracting ${framework_name}.framework from XCFramework..." 542 543 # Find the macOS framework inside the XCFramework 544 local macos_framework=$(find "$xcframework" -name "${framework_name}.framework" -path "*/macos-*" | head -1) 545 if [ -d "$macos_framework" ]; then 546 cp -R "$macos_framework" "${BUILD_OUTPUT_DIR}/Frameworks/" 547 print_info "Copied ${framework_name}.framework to Frameworks directory" 548 else 549 print_warning "Could not find macOS framework in ${xcframework}" 550 fi 551 fi 552 done 553 print_success "Frameworks directory recreated from XCFrameworks" 554 else 555 print_info "Frameworks directory already exists" 556 fi 557 558 # Create final deployment package in temporary directory 559 print_info "Creating final deployment package..." 560 local final_package_name="AXe-Final-$(date +%Y%m%d-%H%M%S)" 561 local final_package_dir="${TEMP_DIR}/${final_package_name}" 562 local final_package_zip="${TEMP_DIR}/${final_package_name}.zip" 563 564 # Create final package directory 565 mkdir -p "${final_package_dir}" 566 567 # Copy notarized executable and frameworks to final package 568 cp "${BUILD_OUTPUT_DIR}/axe" "${final_package_dir}/" 569 if [ -d "${BUILD_OUTPUT_DIR}/Frameworks" ]; then 570 cp -R "${BUILD_OUTPUT_DIR}/Frameworks" "${final_package_dir}/" 571 print_info "Included Frameworks directory in final package" 572 else 573 print_info "No Frameworks directory found - creating executable-only package" 574 fi 575 576 # Create final zip package 577 print_info "Creating final package: ${final_package_zip}" 578 (cd "${TEMP_DIR}" && zip -r "${final_package_name}.zip" "${final_package_name}/") 579 580 # Clean up temporary package directory 581 rm -rf "${final_package_dir}" 582 583 if [ -f "${final_package_zip}" ]; then 584 print_success "Final deployment package created: ${final_package_zip}" 585 586 # Clean up build artifacts (axe executable and Frameworks, keep XCFrameworks) 587 print_info "Cleaning up build artifacts..." 588 rm -f "${BUILD_OUTPUT_DIR}/axe" 589 rm -rf "${BUILD_OUTPUT_DIR}/Frameworks" 590 print_success "Cleaned up axe executable and Frameworks directory" 591 print_info "Preserved XCFrameworks directory for Swift package builds" 592 593 # Output the final package path 594 echo "" 595 echo "📦 Final Package Location:" 596 echo "${final_package_zip}" 597 echo "" 598 599 # Update the global PACKAGE_ZIP variable 600 PACKAGE_ZIP="${final_package_zip}" 601 else 602 echo "❌ Error: Failed to create final deployment package" 603 exit 1 604 fi 605 606 # Clean up temporary extraction directory and original notarization package 607 rm -rf "${temp_extract_dir}" 608 rm -f "${package_zip}" 609 print_info "Cleaned up temporary notarization files" 610 else 611 echo "❌ Error: Could not find notarized executable in package" 612 exit 1 613 fi 614 else 615 echo "❌ Error: Notarization failed" 616 617 # Extract submission ID for log fetching 618 local submission_id=$(echo "${submit_output}" | grep "id:" | head -1 | awk '{print $2}') 619 620 if [ -n "${submission_id}" ]; then 621 print_info "Submission ID: ${submission_id}" 622 print_info "Fetching notarization log for detailed error information..." 623 624 # Fetch the notary log using notarytool 625 echo "" 626 echo "📋 Notarization Log:" 627 echo "$(printf '·%.0s' {1..60})" 628 xcrun notarytool log \ 629 --key "${NOTARIZATION_API_KEY_PATH}" \ 630 --key-id "${NOTARIZATION_KEY_ID}" \ 631 --issuer "${NOTARIZATION_ISSUER_ID}" \ 632 "${submission_id}" 633 echo "$(printf '·%.0s' {1..60})" 634 else 635 print_info "Could not extract submission ID from notarization output" 636 fi 637 638 exit 1 639 fi 640} 641 642# Function to print usage information 643function print_usage() { 644cat <<EOF 645./build.sh usage: 646 ./build.sh [<command>] [<options>]* 647 648Commands: 649 help 650 Print this usage information. 651 652 setup 653 Clone the IDB repository and set up directories. 654 655 clean 656 Clean previous build products and derived data. 657 658 frameworks 659 Build all IDB frameworks (FBControlCore, XCTestBootstrap, FBSimulatorControl, FBDeviceControl). 660 661 install 662 Install built frameworks to the Frameworks directory. 663 664 strip 665 Strip nested frameworks from the built frameworks. 666 667 sign-frameworks 668 Code sign all frameworks with Developer ID. 669 670 xcframeworks 671 Create XCFrameworks from the built frameworks. 672 673 sign-xcframeworks 674 Code sign all XCFrameworks with Developer ID. 675 676 executable 677 Build the AXe executable using Swift Package Manager. 678 679 sign-executable 680 Code sign the AXe executable with Developer ID. 681 682 package 683 Create a notarization package (zip file). 684 685 notarize 686 Submit package for Apple notarization and replace original executable. 687 688 build (default) 689 Run all steps from setup through notarization. 690 691Environment Variables: 692 IDB_CHECKOUT_DIR Directory for IDB repository (default: ./idb_checkout) 693 BUILD_OUTPUT_DIR Directory for build outputs (default: ./build_products) 694 DERIVED_DATA_PATH Directory for derived data (default: ./build_derived_data) 695 TEMP_DIR Temporary directory for final packages (default: system temp) 696 NOTARIZATION_API_KEY_PATH Path to notarization API key (default: ./keys/AuthKey_8TJYVXVDQ6.p8) 697 NOTARIZATION_KEY_ID Notarization key ID (default: 8TJYVXVDQ6) 698 NOTARIZATION_ISSUER_ID Notarization issuer ID (default: 69a6de8e-e388-47e3-e053-5b8c7c11a4d1) 699 700Examples: 701 ./build.sh # Build everything (default) 702 ./build.sh help # Show this help 703 ./build.sh frameworks # Only build frameworks 704 ./build.sh sign-frameworks # Only sign frameworks 705 ./build.sh notarize # Only run notarization step 706EOF 707} 708 709# Individual command functions 710function cmd_setup() { 711 print_section "📥" "Repository Setup" 712 clone_idb_repo 713} 714 715function cmd_clean() { 716 print_section "🧹" "Cleaning Previous Build Products" 717 print_info "Cleaning previous build products and derived data..." 718 rm -rf "${BUILD_OUTPUT_DIR}" 719 rm -rf "${DERIVED_DATA_PATH}" 720 mkdir -p "${BUILD_OUTPUT_DIR}" 721 mkdir -p "${BUILD_XCFRAMEWORK_DIR}" 722 mkdir -p "${DERIVED_DATA_PATH}" 723 print_success "Build directories cleaned and recreated" 724} 725 726function cmd_frameworks() { 727 print_section "🔧" "Building Frameworks" 728 framework_build "FBControlCore" "${FBSIMCONTROL_PROJECT}" "${BUILD_OUTPUT_DIR}" 729 framework_build "XCTestBootstrap" "${FBSIMCONTROL_PROJECT}" "${BUILD_OUTPUT_DIR}" 730 framework_build "FBSimulatorControl" "${FBSIMCONTROL_PROJECT}" "${BUILD_OUTPUT_DIR}" 731 framework_build "FBDeviceControl" "${FBSIMCONTROL_PROJECT}" "${BUILD_OUTPUT_DIR}" 732} 733 734function cmd_install() { 735 print_section "📦" "Installing Frameworks" 736 install_framework "FBControlCore" "${BUILD_OUTPUT_DIR}" 737 install_framework "XCTestBootstrap" "${BUILD_OUTPUT_DIR}" 738 install_framework "FBSimulatorControl" "${BUILD_OUTPUT_DIR}" 739 install_framework "FBDeviceControl" "${BUILD_OUTPUT_DIR}" 740} 741 742function cmd_strip() { 743 print_section "✂️" "Stripping Nested Frameworks" 744 strip_framework "${BUILD_OUTPUT_DIR}" "FBSimulatorControl.framework/Versions/Current/Frameworks/XCTestBootstrap.framework" 745 strip_framework "${BUILD_OUTPUT_DIR}" "FBSimulatorControl.framework/Versions/Current/Frameworks/FBControlCore.framework" 746 strip_framework "${BUILD_OUTPUT_DIR}" "FBDeviceControl.framework/Versions/Current/Frameworks/XCTestBootstrap.framework" 747 strip_framework "${BUILD_OUTPUT_DIR}" "FBDeviceControl.framework/Versions/Current/Frameworks/FBControlCore.framework" 748 strip_framework "${BUILD_OUTPUT_DIR}" "XCTestBootstrap.framework/Versions/Current/Frameworks/FBControlCore.framework" 749} 750 751function cmd_sign_frameworks() { 752 print_section "🔒" "Resigning Frameworks" 753 print_info "Resigning frameworks..." 754 resign_framework "${BUILD_OUTPUT_DIR}" "FBSimulatorControl.framework" 755 resign_framework "${BUILD_OUTPUT_DIR}" "FBDeviceControl.framework" 756 resign_framework "${BUILD_OUTPUT_DIR}" "XCTestBootstrap.framework" 757 resign_framework "${BUILD_OUTPUT_DIR}" "FBControlCore.framework" 758 print_success "Frameworks resigned successfully" 759} 760 761function cmd_xcframeworks() { 762 print_section "📦" "Creating XCFrameworks" 763 create_xcframework "FBControlCore" "${BUILD_OUTPUT_DIR}" 764 create_xcframework "XCTestBootstrap" "${BUILD_OUTPUT_DIR}" 765 create_xcframework "FBSimulatorControl" "${BUILD_OUTPUT_DIR}" 766 create_xcframework "FBDeviceControl" "${BUILD_OUTPUT_DIR}" 767} 768 769function cmd_sign_xcframeworks() { 770 print_section "🔒" "Resigning XCFrameworks" 771 print_info "Resigning XCFrameworks with Developer ID..." 772 resign_xcframework "${BUILD_OUTPUT_DIR}" "FBControlCore.xcframework" 773 resign_xcframework "${BUILD_OUTPUT_DIR}" "XCTestBootstrap.xcframework" 774 resign_xcframework "${BUILD_OUTPUT_DIR}" "FBSimulatorControl.xcframework" 775 resign_xcframework "${BUILD_OUTPUT_DIR}" "FBDeviceControl.xcframework" 776 print_success "XCFrameworks resigned successfully" 777} 778 779function cmd_executable() { 780 print_section "⚡" "Building AXe Executable" 781 build_axe_executable "${BUILD_OUTPUT_DIR}" 782} 783 784function cmd_sign_executable() { 785 print_section "🔒" "Signing AXe Executable" 786 sign_axe_executable "${BUILD_OUTPUT_DIR}" 787} 788 789function cmd_package() { 790 print_section "📦" "Packaging for Notarization" 791 PACKAGE_ZIP=$(package_for_notarization "${BUILD_OUTPUT_DIR}") 792 print_info "Package created: ${PACKAGE_ZIP}" 793} 794 795function cmd_notarize() { 796 print_section "🍎" "Apple Notarization" 797 if [ -z "${PACKAGE_ZIP}" ]; then 798 # Find the most recent package if PACKAGE_ZIP isn't set 799 PACKAGE_ZIP=$(ls -t "${BUILD_OUTPUT_DIR}"/AXe-*.zip 2>/dev/null | head -1) 800 if [ -z "${PACKAGE_ZIP}" ]; then 801 echo "❌ Error: No package found. Run 'package' command first." 802 exit 1 803 fi 804 print_info "Using package: ${PACKAGE_ZIP}" 805 fi 806 notarize_package "${PACKAGE_ZIP}" 807} 808 809function cmd_build() { 810 print_section "🚀" "IDB Framework Builder for AXe Project" 811 812 print_info "IDB Checkout Directory: ${IDB_CHECKOUT_DIR}" 813 print_info "Build Output Directory: ${BUILD_OUTPUT_DIR}" 814 print_info "Derived Data Path: ${DERIVED_DATA_PATH}" 815 print_info "XCFramework Output Directory: ${BUILD_XCFRAMEWORK_DIR}" 816 print_info "Temporary Directory: ${TEMP_DIR}" 817 print_info "IDB Project: ${FBSIMCONTROL_PROJECT}" 818 print_info "Notarization API Key: ${NOTARIZATION_API_KEY_PATH}" 819 print_info "Notarization Key ID: ${NOTARIZATION_KEY_ID}" 820 821 # Run all steps 822 cmd_setup 823 cmd_clean 824 cmd_frameworks 825 cmd_install 826 cmd_strip 827 cmd_sign_frameworks 828 cmd_xcframeworks 829 cmd_sign_xcframeworks 830 cmd_executable 831 cmd_sign_executable 832 cmd_package 833 cmd_notarize 834 835 print_section "🎉" "Build Complete!" 836 print_success "All framework builds, XCFramework creation, AXe executable, and notarization completed." 837 print_info "📦 XCFrameworks are located in ${BUILD_XCFRAMEWORK_DIR}" 838 print_info "📁 Final deployment package is located at ${PACKAGE_ZIP}" 839 print_info "🧹 Build artifacts (axe executable and Frameworks) have been cleaned up" 840 echo "" 841 echo "🏁 Build process finished successfully!" 842 echo "" 843} 844 845# Parse command line arguments 846COMMAND="${1:-build}" 847 848case $COMMAND in 849 help) 850 print_usage 851 exit 0;; 852 setup) 853 cmd_setup;; 854 clean) 855 cmd_clean;; 856 frameworks) 857 cmd_frameworks;; 858 install) 859 cmd_install;; 860 strip) 861 cmd_strip;; 862 sign-frameworks) 863 cmd_sign_frameworks;; 864 xcframeworks) 865 cmd_xcframeworks;; 866 sign-xcframeworks) 867 cmd_sign_xcframeworks;; 868 executable) 869 cmd_executable;; 870 sign-executable) 871 cmd_sign_executable;; 872 package) 873 cmd_package;; 874 notarize) 875 cmd_notarize;; 876 build) 877 cmd_build;; 878 *) 879 echo "Unknown command: $COMMAND" 880 echo "" 881 print_usage 882 exit 1;; 883esac 884 885exit 0