this repo has no description
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