#!/bin/bash # Get the user's home directory USER_HOME="$HOME" # Set up Unreal Engine paths UE_ROOT="/Users/Shared/Epic Games/UE_5.5" UE_EDITOR="$UE_ROOT/Engine/Binaries/Mac/UnrealEditor.app/Contents/MacOS/UnrealEditor" UE_UAT="$UE_ROOT/Engine/Build/BatchFiles/RunUAT.command" # Set up project paths PROJECT_ROOT="$(pwd)" PROJECT_FILE="$PROJECT_ROOT/LuckyWorld.uproject" ARCHIVE_DIR="$PROJECT_ROOT/Builds" # Check for entitlements file if [ -f "$PROJECT_ROOT/LuckyWorld.entitlements" ]; then ENTITLEMENTS_FILE="$PROJECT_ROOT/LuckyWorld.entitlements" else echo "Warning: No entitlements file found. This might affect notarization." ENTITLEMENTS_FILE="" fi # For debugging: print paths and config echo "Project root: $PROJECT_ROOT" echo "Project file: $PROJECT_FILE" echo "Archive directory: $ARCHIVE_DIR" echo "Entitlements file: $ENTITLEMENTS_FILE" # Clean up previous build artifacts rm -rf DerivedDataCache Intermediate Binaries Saved # Generate project files "$UE_ROOT/Engine/Build/BatchFiles/Mac/GenerateProjectFiles.sh" -project="$PROJECT_FILE" -game -engine # Run the build command "$UE_UAT" -ScriptsForProject="$PROJECT_FILE" Turnkey \ -command=VerifySdk \ -platform=Mac \ -UpdateIfNeeded \ -EditorIO \ -EditorIOPort=59484 \ -project="$PROJECT_FILE" \ BuildCookRun \ -nop4 \ -utf8output \ -cook \ -project="$PROJECT_FILE" \ -target=LuckyWorld \ -unrealexe="$UE_EDITOR" \ -platform=Mac \ -installed \ -stage \ -archive \ -package \ -build \ -iterativecooking \ -pak \ -iostore \ -compressed \ -prereqs \ -archivedirectory="$ARCHIVE_DIR" \ -CrashReporter \ -clientconfig=Shipping \ # -nocompile \ # -nocompileuat \ # -nocompileeditor \ # -skipbuildeditor \ # enable these if you want to test build without pak and iostore (you're just testing the build) # -skipiostore \ # -skippak \ (disable -pak and -iostore) echo "" echo "🦾 Build completed. Application path:" APP_PATH=$(find "$ARCHIVE_DIR" -name "*.app" -type d | head -n 1) echo "$APP_PATH" # Check if the build actually succeeded by verifying the app exists if [ -z "$APP_PATH" ] || [ ! -d "$APP_PATH" ]; then echo "❌ ERROR: Build failed or did not produce an app bundle!" echo "Check the logs above for build errors." echo "Common issues:" echo " - 'Targets with a unique build environment cannot be built with an installed engine'" echo " Fix: Use bOverrideBuildEnvironment = true instead of BuildEnvironment = TargetBuildEnvironment.Unique" exit 1 fi if [ -n "$APP_PATH" ]; then echo "" echo "🔍 Binary files that will need signing:" DYLIB_COUNT=$(find "$APP_PATH" -name "*.dylib" | wc -l) SO_COUNT=$(find "$APP_PATH" -name "*.so" | wc -l) FRAMEWORKS=$(find "$APP_PATH" -path "*.framework/*" -type f -perm +111 | wc -l) EXECUTABLES=$(find "$APP_PATH" -type f -perm +111 -not -path "*.framework/*" -not -name "*.dylib" -not -name "*.so" | wc -l) echo "- $DYLIB_COUNT .dylib libraries" echo "- $SO_COUNT .so libraries" echo "- $FRAMEWORKS framework executables" echo "- $EXECUTABLES other executables" echo "Total binary files: $((DYLIB_COUNT + SO_COUNT + FRAMEWORKS + EXECUTABLES))" echo "" echo "🔍 Checking for PhysX and other special libraries (often need special handling):" find "$APP_PATH" -name "*PhysX*" -o -name "*APEX*" fi # Update bundle ID in project settings echo "" echo "🔧 Checking for bundle ID in UE config..." CONFIG_FILE="$PROJECT_ROOT/Config/DefaultGame.ini" if [ -f "$CONFIG_FILE" ]; then if grep -q "\[/Script/MacTargetPlatform\.MacTargetSettings\]" "$CONFIG_FILE" && grep -q "BundleIdentifier=com.luckyrobots.luckyworld" "$CONFIG_FILE"; then echo "Bundle ID already correctly set in project config ✅" else echo "⚠️ Warning: Bundle ID may not be correctly set in DefaultGame.ini" echo "Please ensure [/Script/MacTargetPlatform.MacTargetSettings] section exists with BundleIdentifier=com.luckyrobots.luckyworld" fi else echo "⚠️ Config file not found at $CONFIG_FILE" fi # Post-build process - set bundle ID echo "" echo "🔍 Checking bundle ID in built app..." echo "Note: Bundle ID should be automatically set by Unreal Engine based on DefaultGame.ini and" echo "LuckyWorld.Build.cs settings, but UE sometimes fails to apply it correctly." echo "Therefore, we keep this check and fix as a safety measure." if [ -n "$APP_PATH" ]; then INFO_PLIST="$APP_PATH/Contents/Info.plist" if [ -f "$INFO_PLIST" ]; then CURRENT_BUNDLE_ID=$(/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "$INFO_PLIST") echo "Current bundle ID: $CURRENT_BUNDLE_ID" if [ "$CURRENT_BUNDLE_ID" != "com.luckyrobots.luckyworld" ]; then echo "Bundle ID mismatch - fixing it!" echo "Setting bundle identifier to com.luckyrobots.luckyworld" /usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier com.luckyrobots.luckyworld" "$INFO_PLIST" echo "Updated bundle ID: $(/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "$INFO_PLIST")" else echo "✅ Bundle ID is already correct: com.luckyrobots.luckyworld" fi else echo "⚠️ Info.plist not found at $INFO_PLIST" fi fi # If this is a manual build (not in CI), attempt to sign the app locally if [ -z "$CI" ] && [ -n "$APP_PATH" ]; then echo "" echo "🔐 Attempting local code signing and stapling..." # Check if we have a valid Apple Developer identity IDENTITY=$(security find-identity -v -p codesigning | grep "Developer ID Application" | head -1 | sed -E 's/.*\) ([A-F0-9]+) "(.*)"/\2/') if [ -n "$IDENTITY" ]; then echo "Found signing identity: $IDENTITY" # Sign the app echo "Signing application..." if [ -f "$PROJECT_ROOT/LuckyWorld.entitlements" ]; then echo "Using entitlements file: $PROJECT_ROOT/LuckyWorld.entitlements" codesign --force --options runtime --entitlements "$PROJECT_ROOT/LuckyWorld.entitlements" --sign "$IDENTITY" --deep "$APP_PATH" else codesign --force --options runtime --sign "$IDENTITY" --deep "$APP_PATH" fi # Verify signature echo "Verifying signature..." codesign -vvv --deep "$APP_PATH" # Staple the app if notarization is successful echo "Checking if notarization is needed..." if xcrun altool --notarization-info $(uuidgen) -u "YOUR_APPLE_ID" 2>&1 | grep -q "success"; then echo "App is notarized, stapling the ticket..." xcrun stapler staple "$APP_PATH" xcrun stapler validate "$APP_PATH" # Remove quarantine attribute if present if [ -n "$(xattr -l "$APP_PATH" | grep quarantine)" ]; then echo "Removing quarantine attribute..." xattr -d com.apple.quarantine "$APP_PATH" fi else echo "App is not notarized yet. Upload to Apple's notary service for full verification." fi else echo "⚠️ No Developer ID Application certificate found for signing." echo "Run 'security find-identity -v -p codesigning' to view available certificates." fi else echo "Skipping local signing (running in CI or app not found)" fi # Find and check nested app bundles (like CrashReportClient.app) NESTED_APPS=$(find "$APP_PATH" -name "*.app" -type d | grep -v "^$APP_PATH$") if [ -n "$NESTED_APPS" ]; then echo "Checking nested app bundles:" echo "$NESTED_APPS" | while read -r NESTED_APP; do if [ -f "$NESTED_APP/Contents/Info.plist" ]; then NESTED_NAME=$(basename "$NESTED_APP" .app) NESTED_BUNDLE_ID="com.luckyrobots.luckyworld.$NESTED_NAME" CURRENT_NESTED_ID=$(/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "$NESTED_APP/Contents/Info.plist") if [ "$CURRENT_NESTED_ID" != "$NESTED_BUNDLE_ID" ]; then echo "Setting nested bundle ID to $NESTED_BUNDLE_ID for $NESTED_APP" /usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $NESTED_BUNDLE_ID" "$NESTED_APP/Contents/Info.plist" echo "Updated nested app bundle ID: $(/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "$NESTED_APP/Contents/Info.plist")" else echo "Nested app bundle ID already correct: $CURRENT_NESTED_ID" fi fi done fi echo "" echo "✅ Build and post-processing completed!" echo "App location: $APP_PATH"