Unser Beispielprojekt hat zwei Build-Ziele: HelloWorld.app und Helper.app. Wir erstellen für jedes ein Komponentenpaket und kombinieren es zu einem Produktarchiv .
Ein Komponentenpaket enthält Nutzdaten, die vom OS X-Installationsprogramm installiert werden sollen. Obwohl ein Komponentenpaket einzeln installiert werden kann, wird es normalerweise in ein Produktarchiv integriert .
Nach einem erfolgreichen "Erstellen und Archivieren" öffnen Sie $ BUILT_PRODUCTS_DIR im Terminal.
$ cd ~/Library/Developer/Xcode/DerivedData/.../InstallationBuildProductsLocation
$ pkgbuild --analyze --root ./HelloWorld.app HelloWorldAppComponents.plist
$ pkgbuild --analyze --root ./Helper.app HelperAppComponents.plist
Dies gibt uns die Komponentenliste, die Wertbeschreibung finden Sie im Abschnitt "Komponenteneigenschaftsliste" . pkgbuild -root generiert die Komponentenpakete . Wenn Sie keine der Standardeigenschaften ändern müssen, können Sie den Parameter --component-plist im folgenden Befehl weglassen .
productbuild --synthesize führt zu einer Verteilungsdefinition .
$ pkgbuild --root ./HelloWorld.app \
--component-plist HelloWorldAppComponents.plist \
HelloWorld.pkg
$ pkgbuild --root ./Helper.app \
--component-plist HelperAppComponents.plist \
Helper.pkg
$ productbuild --synthesize \
--package HelloWorld.pkg --package Helper.pkg \
Distribution.xml
In der Distribution.xml können Sie Titel, Hintergrund, Begrüßung, Readme-Datei, Lizenz usw. ändern. Mit diesem Befehl verwandeln Sie Ihre Komponentenpakete und Distributionsdefinitionen in ein Produktarchiv :
$ productbuild --distribution ./Distribution.xml \
--package-path . \
./Installer.pkg
Ich empfehle, einen Blick auf iTunes Installers Distribution.xml zu werfen, um zu sehen, was möglich ist. Sie können "iTunes.pkg installieren" extrahieren mit:
$ pkgutil --expand "Install iTunes.pkg" "Install iTunes"
Lass es uns zusammenstellen
Normalerweise habe ich in meinem Projekt einen Ordner namens Package, der Dinge wie Distribution.xml, Komponentenlisten, Ressourcen und Skripte enthält.
Fügen Sie eine Run Script Build-Phase mit dem Namen "Generate Package" hinzu, die nur bei der Installation auf Run Script eingestellt ist :
VERSION=$(defaults read "${BUILT_PRODUCTS_DIR}/${FULL_PRODUCT_NAME}/Contents/Info" CFBundleVersion)
PACKAGE_NAME=`echo "$PRODUCT_NAME" | sed "s/ /_/g"`
TMP1_ARCHIVE="${BUILT_PRODUCTS_DIR}/$PACKAGE_NAME-tmp1.pkg"
TMP2_ARCHIVE="${BUILT_PRODUCTS_DIR}/$PACKAGE_NAME-tmp2"
TMP3_ARCHIVE="${BUILT_PRODUCTS_DIR}/$PACKAGE_NAME-tmp3.pkg"
ARCHIVE_FILENAME="${BUILT_PRODUCTS_DIR}/${PACKAGE_NAME}.pkg"
pkgbuild --root "${INSTALL_ROOT}" \
--component-plist "./Package/HelloWorldAppComponents.plist" \
--scripts "./Package/Scripts" \
--identifier "com.test.pkg.HelloWorld" \
--version "$VERSION" \
--install-location "/" \
"${BUILT_PRODUCTS_DIR}/HelloWorld.pkg"
pkgbuild --root "${BUILT_PRODUCTS_DIR}/Helper.app" \
--component-plist "./Package/HelperAppComponents.plist" \
--identifier "com.test.pkg.Helper" \
--version "$VERSION" \
--install-location "/" \
"${BUILT_PRODUCTS_DIR}/Helper.pkg"
productbuild --distribution "./Package/Distribution.xml" \
--package-path "${BUILT_PRODUCTS_DIR}" \
--resources "./Package/Resources" \
"${TMP1_ARCHIVE}"
pkgutil --expand "${TMP1_ARCHIVE}" "${TMP2_ARCHIVE}"
# Patches and Workarounds
pkgutil --flatten "${TMP2_ARCHIVE}" "${TMP3_ARCHIVE}"
productsign --sign "Developer ID Installer: John Doe" \
"${TMP3_ARCHIVE}" "${ARCHIVE_FILENAME}"
Wenn Sie das Paket nach der Generierung mit productbuild nicht ändern müssen, können Sie die Schritte pkgutil --expand
und entfernenpkgutil --flatten
. Sie können auch den Parameter --sign für productbuild verwenden, anstatt productsign auszuführen .
Signieren Sie ein OS X-Installationsprogramm
Pakete werden mit dem Developer ID Installer- Zertifikat signiert , das Sie vom Developer Certificate Utility herunterladen können .
Die Signierung erfolgt mit den --sign "Developer ID Installer: John Doe"
Parametern pkgbuild , productbuild oder productsign .
Beachten Sie , dass es keinen Grund gibt, die Komponentenpakete zu signieren , wenn Sie mit productbuild ein signiertes Produktarchiv erstellen .
Den ganzen Weg: Paket in das Xcode-Archiv kopieren
Um etwas in das Xcode-Archiv zu kopieren, können wir die Run Script Build Phase nicht verwenden . Dazu müssen wir eine Schema-Aktion verwenden.
Schema bearbeiten und Archiv erweitern. Klicken Sie dann auf Nachaktionen und fügen Sie eine neue Ausführungsskriptaktion hinzu :
In Xcode 6:
#!/bin/bash
PACKAGES="${ARCHIVE_PATH}/Packages"
PACKAGE_NAME=`echo "$PRODUCT_NAME" | sed "s/ /_/g"`
ARCHIVE_FILENAME="$PACKAGE_NAME.pkg"
PKG="${OBJROOT}/../BuildProductsPath/${CONFIGURATION}/${ARCHIVE_FILENAME}"
if [ -f "${PKG}" ]; then
mkdir "${PACKAGES}"
cp -r "${PKG}" "${PACKAGES}"
fi
Verwenden Sie in Xcode 5 PKG
stattdessen diesen Wert für :
PKG="${OBJROOT}/ArchiveIntermediates/${TARGET_NAME}/BuildProductsPath/${CONFIGURATION}/${ARCHIVE_FILENAME}"
Falls Ihre Versionskontrolle keine Xcode-Schema-Informationen speichert, empfehle ich, diese als Shell-Skript zu Ihrem Projekt hinzuzufügen, damit Sie die Aktion einfach wiederherstellen können, indem Sie das Skript aus dem Arbeitsbereich in die Nachaktion ziehen.
Skripting
Es gibt zwei verschiedene Arten von Skripten : JavaScript in Verteilungsdefinitionsdateien und Shell-Skripte.
Die beste Dokumentation zu Shell-Skripten, die ich in WhiteBox - PackageMaker How-to gefunden habe , aber lesen Sie diese mit Vorsicht, da sie sich auf das alte Paketformat bezieht.
Zusätzliche Lektüre
Bekannte Probleme und Problemumgehungen
Zielauswahlbereich
Dem Benutzer wird die Zielauswahloption mit nur einer einzigen Auswahl angezeigt - "Für alle Benutzer dieses Computers installieren". Die Option wird visuell ausgewählt angezeigt, aber der Benutzer muss darauf klicken, um mit der Installation fortzufahren, was zu Verwirrung führt.
Apples Documentation empfiehlt die Verwendung <domains enable_anywhere ... />
, dies löst jedoch den neuen fehlerhafteren Zielauswahlbereich aus, den Apple in keinem seiner Pakete verwendet.
Wenn Sie das Veraltete verwenden <options rootVolumeOnly="true" />
, erhalten Sie das alte Zielauswahlfenster.
Sie möchten Elemente im Home-Ordner des aktuellen Benutzers installieren.
Kurze Antwort: VERSUCHEN SIE ES NICHT!
Lange Antwort: WIRKLICH; VERSUCHE ES NICHT! Lesen Sie die Installationsprobleme und -lösungen . Weißt du, was ich getan habe, nachdem ich das gelesen habe? Ich war dumm genug, es zu versuchen. Ich bin mir sicher, dass sie die Probleme in 10.7 oder 10.8 behoben haben.
Zunächst sah ich von Zeit zu Zeit den oben genannten Fehler im Zielauswahlbereich. Das hätte mich aufhalten sollen, aber ich habe es ignoriert. Wenn Sie die Woche nach der Veröffentlichung Ihrer Software nicht damit verbringen möchten, Support-E-Mails zu beantworten, auf die sie klicken müssen, sobald die schöne blaue Auswahl angezeigt wird, verwenden Sie diese NICHT.
Sie denken jetzt, dass Ihre Benutzer klug genug sind, um das Panel herauszufinden, nicht wahr? Nun, hier ist eine andere Sache über die Installation von Home-Ordnern: SIE ARBEITEN NICHT!
Ich habe es zwei Wochen lang auf ungefähr 10 verschiedenen Computern mit verschiedenen Betriebssystemversionen getestet und was nicht, und es ist nie fehlgeschlagen. Also habe ich es verschickt. Innerhalb einer Stunde nach der Veröffentlichung habe ich ein Herz von Benutzern, die es einfach nicht installieren konnten. Die Protokolle weisen auf Berechtigungsprobleme hin, die Sie nicht beheben können.
Wiederholen wir es also noch einmal: Wir verwenden den Installer nicht für die Installation von Home-Ordnern!
RTFD für Willkommen, Read-me, Lizenz und Abschluss wird von nicht akzeptiert productbuild
.
Das Installationsprogramm unterstützt von Anfang an RTFD-Dateien, um hübsche Begrüßungsbildschirme mit Bildern zu erstellen, aber Productbuild akzeptiert sie nicht.
Problemumgehungen: Verwenden Sie eine Dummy-RTF-Datei und ersetzen Sie sie im Paket, nachdem productbuild
dies abgeschlossen ist.
Hinweis: Sie können auch Retina-Bilder in der RTFD-Datei haben. Verwenden Sie dazu Multi-Image-TIFF-Dateien : tiffutil -cat Welcome.tif Welcome_2x.tif -out FinalWelcome.tif
. Weitere Details .
Starten einer Anwendung, wenn die Installation mit einem BundlePostInstallScriptPath- Skript abgeschlossen ist:
#!/bin/bash
LOGGED_IN_USER_ID=`id -u "${USER}"`
if [ "${COMMAND_LINE_INSTALL}" = "" ]
then
/bin/launchctl asuser "${LOGGED_IN_USER_ID}" /usr/bin/open -g PATH_OR_BUNDLE_ID
fi
exit 0
Es ist wichtig, die App als angemeldeter Benutzer und nicht als Installationsbenutzer auszuführen. Dies geschieht mit dem UID-Pfad von launchctl asuser . Außerdem führen wir es nur aus, wenn es sich nicht um eine Befehlszeileninstallation handelt, die mit dem Installationsprogramm oder Apple Remote Desktop durchgeführt wird .