Mac Build System und App Packing
This commit is contained in:
Executable
+123
@@ -0,0 +1,123 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# ============================================================
|
||||
# build-macos-app.sh
|
||||
# Baut LehrerApp.Desktop als natives macOS .app-Bundle
|
||||
# ============================================================
|
||||
|
||||
# ---- Konfiguration (bei Bedarf anpassen) --------------------
|
||||
PROJECT_PATH="LehrerApp.Desktop" # Pfad zum .csproj / Projektordner
|
||||
APP_NAME="LehrerApp" # Name des App-Bundles (LehrerApp.app)
|
||||
EXECUTABLE_NAME="LehrerApp.Desktop" # Name der Binary aus dotnet publish (= AssemblyName des Projekts!)
|
||||
BUNDLE_ID="de.deinname.lehrerapp" # CFBundleIdentifier
|
||||
VERSION="1.0.0" # CFBundleShortVersionString
|
||||
MIN_MACOS="12.0" # LSMinimumSystemVersion
|
||||
ICON_PNG="icon.png" # Quell-Icon (min. 512x512 oder 1024x1024)
|
||||
RUNTIME_IDS=("osx-arm64" "osx-x64") # Architekturen, für die gebaut wird
|
||||
UNIVERSAL_BUILD=true # true = zusätzlich Universal Binary via lipo
|
||||
CODESIGN_ADHOC=true # true = Ad-hoc-Signierung am Ende
|
||||
|
||||
# ---- Ableitungen ---------------------------------------------
|
||||
BUILD_DIR="./build"
|
||||
APP_BUNDLE="${BUILD_DIR}/${APP_NAME}.app"
|
||||
CONTENTS_DIR="${APP_BUNDLE}/Contents"
|
||||
MACOS_DIR="${CONTENTS_DIR}/MacOS"
|
||||
RESOURCES_DIR="${CONTENTS_DIR}/Resources"
|
||||
|
||||
echo "=== 1/6: Alte Build-Artefakte entfernen ==="
|
||||
rm -rf "${BUILD_DIR}"
|
||||
mkdir -p "${BUILD_DIR}"
|
||||
|
||||
echo "=== 2/6: dotnet publish für jede Architektur ==="
|
||||
for RID in "${RUNTIME_IDS[@]}"; do
|
||||
echo " -> Publish für ${RID}"
|
||||
dotnet publish "${PROJECT_PATH}" \
|
||||
-c Release \
|
||||
-r "${RID}" \
|
||||
--self-contained true \
|
||||
-p:PublishSingleFile=false \
|
||||
-o "${BUILD_DIR}/publish/${RID}"
|
||||
done
|
||||
|
||||
echo "=== 3/6: App-Bundle-Struktur anlegen ==="
|
||||
mkdir -p "${MACOS_DIR}" "${RESOURCES_DIR}"
|
||||
|
||||
if [ "${UNIVERSAL_BUILD}" = true ] && [ "${#RUNTIME_IDS[@]}" -ge 2 ]; then
|
||||
echo " -> Universal Binary via lipo erzeugen"
|
||||
SRC_A="${BUILD_DIR}/publish/${RUNTIME_IDS[0]}/${EXECUTABLE_NAME}"
|
||||
SRC_B="${BUILD_DIR}/publish/${RUNTIME_IDS[1]}/${EXECUTABLE_NAME}"
|
||||
|
||||
# Alle Nicht-Binärdateien (Assets etc.) aus der ersten Architektur übernehmen
|
||||
rsync -a --exclude "${EXECUTABLE_NAME}" "${BUILD_DIR}/publish/${RUNTIME_IDS[0]}/" "${MACOS_DIR}/"
|
||||
|
||||
lipo -create -output "${MACOS_DIR}/${EXECUTABLE_NAME}" "${SRC_A}" "${SRC_B}"
|
||||
else
|
||||
echo " -> Einzelne Architektur (${RUNTIME_IDS[0]}) übernehmen"
|
||||
cp -r "${BUILD_DIR}/publish/${RUNTIME_IDS[0]}/"* "${MACOS_DIR}/"
|
||||
fi
|
||||
|
||||
if [ ! -f "${MACOS_DIR}/${EXECUTABLE_NAME}" ]; then
|
||||
echo "FEHLER: Erwartete Executable '${EXECUTABLE_NAME}' nicht in ${MACOS_DIR} gefunden."
|
||||
echo "Tatsächlicher Inhalt von ${BUILD_DIR}/publish/${RUNTIME_IDS[0]}/:"
|
||||
ls "${BUILD_DIR}/publish/${RUNTIME_IDS[0]}/"
|
||||
echo "-> Passe EXECUTABLE_NAME im Skript-Kopf entsprechend an."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
chmod +x "${MACOS_DIR}/${EXECUTABLE_NAME}"
|
||||
|
||||
echo "=== 4/6: Icon konvertieren (.icns) ==="
|
||||
if [ -f "${ICON_PNG}" ]; then
|
||||
ICONSET_DIR="${BUILD_DIR}/icon.iconset"
|
||||
mkdir -p "${ICONSET_DIR}"
|
||||
|
||||
sips -z 16 16 "${ICON_PNG}" --out "${ICONSET_DIR}/icon_16x16.png" > /dev/null
|
||||
sips -z 32 32 "${ICON_PNG}" --out "${ICONSET_DIR}/icon_16x16@2x.png" > /dev/null
|
||||
sips -z 32 32 "${ICON_PNG}" --out "${ICONSET_DIR}/icon_32x32.png" > /dev/null
|
||||
sips -z 64 64 "${ICON_PNG}" --out "${ICONSET_DIR}/icon_32x32@2x.png" > /dev/null
|
||||
sips -z 128 128 "${ICON_PNG}" --out "${ICONSET_DIR}/icon_128x128.png" > /dev/null
|
||||
sips -z 256 256 "${ICON_PNG}" --out "${ICONSET_DIR}/icon_128x128@2x.png" > /dev/null
|
||||
sips -z 256 256 "${ICON_PNG}" --out "${ICONSET_DIR}/icon_256x256.png" > /dev/null
|
||||
sips -z 512 512 "${ICON_PNG}" --out "${ICONSET_DIR}/icon_256x256@2x.png" > /dev/null
|
||||
sips -z 512 512 "${ICON_PNG}" --out "${ICONSET_DIR}/icon_512x512.png" > /dev/null
|
||||
sips -z 1024 1024 "${ICON_PNG}" --out "${ICONSET_DIR}/icon_512x512@2x.png" > /dev/null
|
||||
|
||||
iconutil -c icns "${ICONSET_DIR}" -o "${RESOURCES_DIR}/icon.icns"
|
||||
ICON_KEY="<key>CFBundleIconFile</key><string>icon.icns</string>"
|
||||
else
|
||||
echo " -> Kein '${ICON_PNG}' gefunden, App-Bundle wird ohne Icon erstellt."
|
||||
ICON_KEY=""
|
||||
fi
|
||||
|
||||
echo "=== 5/6: Info.plist schreiben ==="
|
||||
cat > "${CONTENTS_DIR}/Info.plist" <<PLIST
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleName</key><string>${APP_NAME}</string>
|
||||
<key>CFBundleDisplayName</key><string>${APP_NAME}</string>
|
||||
<key>CFBundleExecutable</key><string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key><string>${BUNDLE_ID}</string>
|
||||
<key>CFBundlePackageType</key><string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key><string>${VERSION}</string>
|
||||
<key>CFBundleVersion</key><string>${VERSION}</string>
|
||||
<key>LSMinimumSystemVersion</key><string>${MIN_MACOS}</string>
|
||||
<key>NSHighResolutionCapable</key><true/>
|
||||
${ICON_KEY}
|
||||
</dict>
|
||||
</plist>
|
||||
PLIST
|
||||
|
||||
echo "=== 6/6: Signieren ==="
|
||||
if [ "${CODESIGN_ADHOC}" = true ]; then
|
||||
codesign --force --deep --sign - "${APP_BUNDLE}"
|
||||
echo " -> Ad-hoc signiert (nur lokal lauffähig, Gatekeeper-Warnung bei Weitergabe)."
|
||||
else
|
||||
echo " -> Signierung übersprungen."
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Fertig! App-Bundle liegt unter: ${APP_BUNDLE}"
|
||||
echo "Test mit: open '${APP_BUNDLE}'"
|
||||
Reference in New Issue
Block a user