diff --git a/.gitignore b/.gitignore index 57136dc..ac44341 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ bin/ obj/ out/ +build/ # ── .NET ────────────────────────────────────────────────────────────────────── *.user diff --git a/build-macos-app.sh b/build-macos-app.sh new file mode 100755 index 0000000..972a6e7 --- /dev/null +++ b/build-macos-app.sh @@ -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="CFBundleIconFileicon.icns" +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" < + + + + CFBundleName${APP_NAME} + CFBundleDisplayName${APP_NAME} + CFBundleExecutable${EXECUTABLE_NAME} + CFBundleIdentifier${BUNDLE_ID} + CFBundlePackageTypeAPPL + CFBundleShortVersionString${VERSION} + CFBundleVersion${VERSION} + LSMinimumSystemVersion${MIN_MACOS} + NSHighResolutionCapable + ${ICON_KEY} + + +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}'" \ No newline at end of file diff --git a/icon.png b/icon.png new file mode 100644 index 0000000..b4be217 Binary files /dev/null and b/icon.png differ