Skip to content

Commit 1d98929

Browse files
committed
build: add universal build support and notarization workflow
- Add --universal flag for arm64 + x86_64 lipo binary - Add notarization support via notarytool (--apple-id, --team-id, --password) - Sign internal bundles before main executable - Support entitlements file if present - Display architecture info in build summary
1 parent b74c5c9 commit 1d98929

1 file changed

Lines changed: 110 additions & 14 deletions

File tree

build.sh

Lines changed: 110 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@ set -e
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

1215
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
@@ -15,26 +18,56 @@ APP_NAME="DockMaster Pro"
1518
APP_BUNDLE="${APP_NAME}.app"
1619
BUILD_DIR="${SCRIPT_DIR}/build"
1720
INFO_PLIST="${SCRIPT_DIR}/${TARGET}/App/Info.plist"
21+
ENTITLEMENTS="${SCRIPT_DIR}/DockMasterPro.entitlements"
1822

1923
# ── Parse arguments ───────────────────────────
2024
CONFIGURATION="release"
2125
SIGN_IDENTITY=""
26+
APPLE_ID=""
27+
TEAM_ID=""
28+
APP_PASSWORD=""
29+
UNIVERSAL=false
2230

2331
while [[ $# -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
2941
done
3042

31-
SPM_BUILD_DIR="${SCRIPT_DIR}/.build/arm64-apple-macosx/${CONFIGURATION}"
3243
APP_PATH="${BUILD_DIR}/${APP_BUNDLE}"
3344

3445
# ── Step 1: Compile ───────────────────────────
3546
echo "▶ 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 ──────────────
4073
echo "▶ Assembling ${APP_BUNDLE}..."
@@ -43,15 +76,25 @@ mkdir -p "${APP_PATH}/Contents/MacOS"
4376
mkdir -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
4986
cp "${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
57100
ICNS="${SCRIPT_DIR}/${TARGET}/Resources/AppIcon.icns"
@@ -62,9 +105,57 @@ echo "✓ Bundle assembled: ${APP_PATH}"
62105
# ── Step 3: Code sign ─────────────────────────
63106
if [ -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
68159
else
69160
echo "▶ Ad-hoc signing (local use only)..."
70161
codesign --force --deep --sign - "${APP_PATH}"
@@ -73,7 +164,9 @@ fi
73164

74165
# ── Step 4: Package as zip ────────────────────
75166
VERSION=$(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"
77170
ZIP_PATH="${BUILD_DIR}/${ZIP_NAME}"
78171

79172
echo "▶ Creating ${ZIP_NAME}..."
@@ -89,5 +182,8 @@ echo "│ Build complete │"
89182
echo "├─────────────────────────────────────────┤"
90183
printf "│ App : %-30s │\n" "${APP_PATH##*/Users/shicheng/}"
91184
printf "│ 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}"
92188
printf "│ Config : %-30s │\n" "${CONFIGURATION}"
93189
echo "└─────────────────────────────────────────┘"

0 commit comments

Comments
 (0)