44# ─────────────────────────────────────────────
55# DockMaster Pro — Build Script
66# Usage:
7- # ./build.sh # release build
8- # ./build.sh --debug # debug build
9- # ./build.sh --sign "Developer ID Application: Your Name (TEAMID)"
7+ # ./build.sh # release build (arm64 only)
8+ # ./build.sh --debug # debug build
9+ # ./build.sh --universal # universal build (arm64 + x86_64)
10+ # ./build.sh --sign "Developer ID Application: Your Name (TEAMID)" \
11+ # --apple-id "your@apple.id" --team-id "TEAMID" \
12+ # --password "app-specific-password"
1013# ─────────────────────────────────────────────
1114
1215SCRIPT_DIR=" $( cd " $( dirname " $0 " ) " && pwd) "
@@ -15,26 +18,56 @@ APP_NAME="DockMaster Pro"
1518APP_BUNDLE=" ${APP_NAME} .app"
1619BUILD_DIR=" ${SCRIPT_DIR} /build"
1720INFO_PLIST=" ${SCRIPT_DIR} /${TARGET} /App/Info.plist"
21+ ENTITLEMENTS=" ${SCRIPT_DIR} /DockMasterPro.entitlements"
1822
1923# ── Parse arguments ───────────────────────────
2024CONFIGURATION=" release"
2125SIGN_IDENTITY=" "
26+ APPLE_ID=" "
27+ TEAM_ID=" "
28+ APP_PASSWORD=" "
29+ UNIVERSAL=false
2230
2331while [[ $# -gt 0 ]]; do
2432 case " $1 " in
25- --debug) CONFIGURATION=" debug" ; shift ;;
26- --sign) SIGN_IDENTITY=" $2 " ; shift 2 ;;
27- * ) echo " Unknown option: $1 " ; exit 1 ;;
33+ --debug) CONFIGURATION=" debug" ; shift ;;
34+ --universal) UNIVERSAL=true; shift ;;
35+ --sign) SIGN_IDENTITY=" $2 " ; shift 2 ;;
36+ --apple-id) APPLE_ID=" $2 " ; shift 2 ;;
37+ --team-id) TEAM_ID=" $2 " ; shift 2 ;;
38+ --password) APP_PASSWORD=" $2 " ; shift 2 ;;
39+ * ) echo " Unknown option: $1 " ; exit 1 ;;
2840 esac
2941done
3042
31- SPM_BUILD_DIR=" ${SCRIPT_DIR} /.build/arm64-apple-macosx/${CONFIGURATION} "
3243APP_PATH=" ${BUILD_DIR} /${APP_BUNDLE} "
3344
3445# ── Step 1: Compile ───────────────────────────
3546echo " ▶ Building (${CONFIGURATION} )..."
36- swift build -c " ${CONFIGURATION} " --arch arm64 2>&1
37- echo " ✓ Build succeeded"
47+
48+ if [ " $UNIVERSAL " = true ]; then
49+ echo " Building arm64..."
50+ swift build -c " ${CONFIGURATION} " --arch arm64 2>&1
51+ echo " Building x86_64..."
52+ swift build -c " ${CONFIGURATION} " --arch x86_64 2>&1
53+
54+ ARM64_BIN=" ${SCRIPT_DIR} /.build/arm64-apple-macosx/${CONFIGURATION} /${TARGET} "
55+ X86_64_BIN=" ${SCRIPT_DIR} /.build/x86_64-apple-macosx/${CONFIGURATION} /${TARGET} "
56+
57+ # Create universal binary
58+ UNIVERSAL_BIN=" ${BUILD_DIR} /${TARGET} -universal"
59+ mkdir -p " ${BUILD_DIR} "
60+ lipo -create -output " ${UNIVERSAL_BIN} " " ${ARM64_BIN} " " ${X86_64_BIN} "
61+
62+ SPM_BUNDLE_ARM=" ${SCRIPT_DIR} /.build/arm64-apple-macosx/${CONFIGURATION} "
63+ SPM_BUNDLE_X86=" ${SCRIPT_DIR} /.build/x86_64-apple-macosx/${CONFIGURATION} "
64+
65+ echo " ✓ Universal build succeeded"
66+ else
67+ swift build -c " ${CONFIGURATION} " --arch arm64 2>&1
68+ SPM_BUILD_DIR=" ${SCRIPT_DIR} /.build/arm64-apple-macosx/${CONFIGURATION} "
69+ echo " ✓ Build succeeded"
70+ fi
3871
3972# ── Step 2: Assemble .app bundle ──────────────
4073echo " ▶ Assembling ${APP_BUNDLE} ..."
@@ -43,15 +76,25 @@ mkdir -p "${APP_PATH}/Contents/MacOS"
4376mkdir -p " ${APP_PATH} /Contents/Resources"
4477
4578# Executable
46- cp " ${SPM_BUILD_DIR} /${TARGET} " " ${APP_PATH} /Contents/MacOS/${TARGET} "
79+ if [ " $UNIVERSAL " = true ]; then
80+ cp " ${UNIVERSAL_BIN} " " ${APP_PATH} /Contents/MacOS/${TARGET} "
81+ else
82+ cp " ${SPM_BUILD_DIR} /${TARGET} " " ${APP_PATH} /Contents/MacOS/${TARGET} "
83+ fi
4784
4885# Info.plist
4986cp " ${INFO_PLIST} " " ${APP_PATH} /Contents/Info.plist"
5087
5188# SPM resource bundles (localizations, assets, etc.)
52- for bundle in " ${SPM_BUILD_DIR} " /* .bundle; do
53- [ -e " $bundle " ] && cp -r " $bundle " " ${APP_PATH} /Contents/Resources/"
54- done
89+ if [ " $UNIVERSAL " = true ]; then
90+ for bundle in " ${SPM_BUNDLE_ARM} " /* .bundle; do
91+ [ -e " $bundle " ] && cp -r " $bundle " " ${APP_PATH} /Contents/Resources/"
92+ done
93+ else
94+ for bundle in " ${SPM_BUILD_DIR} " /* .bundle; do
95+ [ -e " $bundle " ] && cp -r " $bundle " " ${APP_PATH} /Contents/Resources/"
96+ done
97+ fi
5598
5699# App icon
57100ICNS=" ${SCRIPT_DIR} /${TARGET} /Resources/AppIcon.icns"
@@ -62,9 +105,57 @@ echo "✓ Bundle assembled: ${APP_PATH}"
62105# ── Step 3: Code sign ─────────────────────────
63106if [ -n " $SIGN_IDENTITY " ]; then
64107 echo " ▶ Signing with: ${SIGN_IDENTITY} "
108+
109+ # Sign internal bundles first
110+ for bundle in " ${APP_PATH} /Contents/Resources" /* .bundle; do
111+ [ -e " $bundle " ] && codesign --force --options runtime \
112+ --sign " $SIGN_IDENTITY " " $bundle "
113+ done
114+
115+ # Sign main executable
116+ if [ -f " $ENTITLEMENTS " ]; then
117+ echo " Using entitlements: ${ENTITLEMENTS} "
118+ codesign --force --options runtime \
119+ --entitlements " $ENTITLEMENTS " \
120+ --sign " $SIGN_IDENTITY " " ${APP_PATH} /Contents/MacOS/${TARGET} "
121+ else
122+ codesign --force --options runtime \
123+ --sign " $SIGN_IDENTITY " " ${APP_PATH} /Contents/MacOS/${TARGET} "
124+ fi
125+
126+ # Sign the app bundle
65127 codesign --force --deep --options runtime \
66128 --sign " $SIGN_IDENTITY " " ${APP_PATH} "
129+
67130 echo " ✓ Signed"
131+
132+ # ── Step 3.5: Notarize ────────────────────
133+ if [ -n " $APPLE_ID " ] && [ -n " $TEAM_ID " ] && [ -n " $APP_PASSWORD " ]; then
134+ echo " ▶ Notarizing..."
135+
136+ # Create a temporary zip for notarization
137+ NOTARIZE_ZIP=" ${BUILD_DIR} /notarize-tmp.zip"
138+ cd " ${BUILD_DIR} "
139+ zip -qr " ${NOTARIZE_ZIP} " " ${APP_BUNDLE} "
140+ cd " ${SCRIPT_DIR} "
141+
142+ # Submit for notarization
143+ xcrun notarytool submit " ${NOTARIZE_ZIP} " \
144+ --apple-id " ${APPLE_ID} " \
145+ --team-id " ${TEAM_ID} " \
146+ --password " ${APP_PASSWORD} " \
147+ --wait
148+
149+ # Staple the notarization ticket
150+ xcrun stapler staple " ${APP_PATH} "
151+
152+ # Clean up temp zip
153+ rm -f " ${NOTARIZE_ZIP} "
154+
155+ echo " ✓ Notarized and stapled"
156+ else
157+ echo " ⚠ Skipping notarization (provide --apple-id, --team-id, --password)"
158+ fi
68159else
69160 echo " ▶ Ad-hoc signing (local use only)..."
70161 codesign --force --deep --sign - " ${APP_PATH} "
73164
74165# ── Step 4: Package as zip ────────────────────
75166VERSION=$( defaults read " ${APP_PATH} /Contents/Info.plist" CFBundleShortVersionString 2> /dev/null || echo " 1.0.0" )
76- ZIP_NAME=" ${TARGET} _v${VERSION} .zip"
167+ ARCH_SUFFIX=" "
168+ [ " $UNIVERSAL " = true ] && ARCH_SUFFIX=" -universal"
169+ ZIP_NAME=" ${TARGET} _v${VERSION}${ARCH_SUFFIX} .zip"
77170ZIP_PATH=" ${BUILD_DIR} /${ZIP_NAME} "
78171
79172echo " ▶ Creating ${ZIP_NAME} ..."
@@ -89,5 +182,8 @@ echo "│ Build complete │"
89182echo " ├─────────────────────────────────────────┤"
90183printf " │ App : %-30s │\n" " ${APP_PATH##*/ Users/ shicheng/ } "
91184printf " │ Zip : %-30s │\n" " ${ZIP_NAME} "
185+ ARCH_INFO=" arm64"
186+ [ " $UNIVERSAL " = true ] && ARCH_INFO=" universal (arm64 + x86_64)"
187+ printf " │ Arch : %-30s │\n" " ${ARCH_INFO} "
92188printf " │ Config : %-30s │\n" " ${CONFIGURATION} "
93189echo " └─────────────────────────────────────────┘"
0 commit comments