Erstellen von macOS Installer-Paketen, die für die Entwickler-ID bereit sind

188

Hinweis: Dies gilt nur für OS X Installer- Pakete. Pakete zur Übermittlung an den Mac App Store unterliegen unterschiedlichen Regeln.

Wegen Mountain Lion's Gatekeeper musste ich endlich mein PackageMaker- Build-Skript hinter die Scheune nehmen und es abschießen. PackageMaker wurde bereits aus Xcode entfernt und in "Auxiliary Tools for Xcode" verschoben, sodass es hoffentlich bald vergessen wird.

Die Frage ist , wie kann ich pkgbuild, productbuildund pkgutilsie ersetzen?

Catlan
quelle
Ich gehe also davon aus, dass das Problem mit Packagemaker darin besteht, dass pkg-Dateien für die Verwendung mit Gatekeeper auf Mountain Lion nicht ordnungsgemäß signiert werden können.
JasonZ
1
Es ist möglich, aber PackageMaker war immer fehlerhaft und wurde mit Mac OS X 10.6 Snow Leopard veraltet. Auf lange Sicht sparen Sie Zeit, um sich mit den neuen Tools vertraut zu machen.
Catlan
@catlan: Haben Sie einen offiziellen Link, der besagt, dass Packagemaker auf 10.6 veraltet ist?
Carl
2
@carleeto: Es wurde nie als veraltet angekündigt, nur aus Xcode entfernt und schließlich wie ein birmanischer Demonstrant "verschwunden".
Fehler

Antworten:

343

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 .

Unsere Tools: pkgbuild , productbuild und pkgutil

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 --expandund 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 .

Dienstprogramm für Entwicklerzertifikate

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 PKGstattdessen 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.

Beispiel für den Installationsfehler

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. Beispiel für einen alten Zielauswahlbereich


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 productbuilddies 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 .


Catlan
quelle
9
Dies ist ein ausgezeichnetes Tutorial, setzt jedoch voraus, dass vorgefertigte Bundles vorhanden sind. /tmpWie strukturiere ich die Komponentenliste, wenn ich beispielsweise eine einzelne Datei installieren würde , die später in einem Postflight-Skript verarbeitet werden soll? Alle verfügbaren Dokumentationen scheinen davon auszugehen, dass der Entwickler sie --analyzezumindest anfänglich mit generiert hat .
Fehler
1
Wenn Sie nichts an dem ändern Component Property Listmüssen, müssen Sie nicht ausführen --analyze. Für die Nachbearbeitung von Dateien empfehle ich, sie alle in ein Paket zu legen und den Installationsort für das Paket auf zu setzen /tmp. Aber vielleicht verstehe ich Ihre Frage falsch. Wenn ja, poste es in einer detaillierteren Version auf SO.
Catlan
2
Bitte beachten Sie, dass es absolut keinen Sinn macht, Pakete über die Befehlszeile zu erstellen und zu versuchen, alle Fehler in der Verpackungs-App zu vermeiden. Lesen Sie lieber meinen Kommentar unten zur Verwendung der Anwendung "Pakete" von Stéphane Sudre, die alle Probleme für Sie löst!
Bram de Jong
5
@BramdeJong "macht keinen Sinn". Ich bin nicht einverstanden. Apple verwaltet die Befehlszeilentools. Packages ist eine Drittanbieter-App, die nicht von der Community unterstützt wird und möglicherweise in Zukunft nicht mehr funktioniert, wenn Apple etwas drastisches ändert. Für mich würde ich lieber die Befehlszeilentechnik kennen, damit ich weiterlaufen kann, wenn Apple etwas drastisches ändert.
Volomike
5
$ pkgbuild --root ./HelloWorld.app ist falsch (vorausgesetzt, .app ist ein tatsächliches App-Bundle). pkgbuild arbeitet mit einem Zielstamm: dh einem Ordner, der ein von der xcode-Toolkette generiertes Bundle enthält. Das Argument für pkgbuild ist also der Pfad zum enthaltenen Ordner des Bundles, das wir verpacken möchten. Wenn dies nicht richtig ausgeführt wird, wird ein Paket erstellt, das nur den Ordner mit den App-Inhalten enthält. Es wird nicht als tatsächliches App-Bundle installiert. Das Werbegeschenk befindet sich in der Komponentenliste. Wenn das keinen RootRelativeBundlePath-Eintrag enthält, der das App-Bundle angibt, haben Sie es vermasselt.
Jonathan Mitchell
185

Es gibt eine sehr interessante Anwendung von Stéphane Sudre, die all dies für Sie erledigt, skriptfähig ist / das Erstellen über die Befehlszeile unterstützt, eine super schöne Benutzeroberfläche hat und KOSTENLOS ist. Traurige Sache ist: Es heißt "Pakete", was es unmöglich macht, in Google zu finden.

http://s.sudre.free.fr/Software/Packages/about.html

Ich wünschte, ich hätte es gewusst, bevor ich anfing, meine eigenen Skripte von Hand zu erstellen.

Screenshot der Paketanwendung

Bram de Jong
quelle
11
Ich kann nicht glauben, dass dieser Beitrag nicht mehr Sinn hat. Diese Software ist erstaunlich und unterstützt das Erstellen über die Befehlszeile.
Cesar Mendoza
1
Hat jemand versucht, mit diesem Tool ein Paket zu signieren? Ich kann den Menüpunkt "Zertifikat
festlegen
2
@ user283182: Sehr spät, sicherlich haben Sie es bereits herausgefunden, aber vielleicht hilft dies anderen - ich denke, das Problem, mit dem Sie konfrontiert sind, ist in den [Richtlinien zur Überprüfung des Mac App Store] ( developer.apple.com/app-) aufgeführt. store / review / policy / mac /… ), Regel 2.14: "Apps müssen mit den in Xcode enthaltenen Apple-Verpackungstechnologien verpackt und eingereicht werden - Installationsprogramme von Drittanbietern sind nicht zulässig."
Elder Elder
4
Ich wünschte, dies wäre eine kostenpflichtige App und der Entwickler aktualisiert und patcht ständig. Packages.app ist fantastisch, besonders wenn Sie Ihre App schnell bereitstellen möchten. Die Installation dauerte insgesamt 3 Minuten. Lesen Sie die Übersicht, richten Sie mein Projekt ein und erstellen Sie das installierbare Paket. Ein großes Lob an Stéphane.
Nikolay Christov
1
Diese App ist super!
Spencer Müller Diniz
3

Zu Ihrer Information: Wenn Sie versuchen, ein Paketinstallationsprogramm für ein Bundle oder Plugin zu erstellen, ist dies ganz einfach:

pkgbuild --component "Color Lists.colorPicker" --install-location ~/Library/ColorPickers ColorLists.pkg
gngrwzrd
quelle
2
Zu Ihrer Information, es gibt einen Unterschied zwischen dem Erstellen eines .pkg und dem Erstellen eines echten Installationsprogramms mit Begrüßungsbildschirm, Lizenz usw.
Catlan
Ja, ich bin mir bewusst, dass ich dies hier eingefügt habe, weil ich keinen Hinweis auf das Erstellen eines pkg-Installationsprogramms für ein Plugin finden konnte.
Gngrwzrd
Das brachte mich ins Rollen. Nur das Minimum, um Erdung zu geben.
Uchuugaka
3

A +1 auf akzeptierte Antwort:

Zielauswahl im Installationsprogramm

Wenn die Auswahl der Domäne (auch als Ziel bezeichnet) zwischen Benutzerdomäne und Systemdomäne gewünscht wird, <domains enable_anywhere="true">verwenden Sie Folgendes, anstatt zu versuchen :

<domains enable_currentUserHome="true" enable_localSystem="true"/>

enable_currentUserHome installiert die Anwendungs-App unter ~/Applications/und enable_localSystemermöglicht die Installation der Anwendung unter/Application

Ich habe dies in El Capitan 10.11.6 (15G1217) versucht und es scheint in 1 Entwicklungsmaschine und 2 verschiedenen VMs, die ich ausprobiert habe, einwandfrei zu funktionieren.

PnotNP
quelle
Dies funktioniert gut, aber mit einem GOT'CHA: Wenn Sie zuerst pro Benutzer und dann pro Computer installieren, erfolgt die Installation im Benutzerverzeichnis, nicht im Maschinenverzeichnis, sondern mit Sudo-Rechten. Das Gegenteil ist nicht der Fall: Sie können pro Computer und anschließend pro Benutzer installieren und an beiden Stellen installieren.
Terje Dahl
@TerjeDahl Ja, dies liegt daran, dass das Bundle nach der Installation an den Speicherort verschoben wird, an dem die gleiche Bundle-ID zuvor vom Installationsprogramm installiert wurde (und das Installationsprogramm weiß das). Dies kann durch einige Einstellungen in der Manifestdatei verhindert werden, an die ich mich momentan nicht erinnere.
PnotNP
@ PnotNP Ah. Wenn Sie so freundlich wären, mit dieser Einstellung zurückzukommen, wenn Sie sich erinnern könnten, dann wäre das großartig!
Terje Dahl
2

Hier ist ein Build-Skript , das ein signiertes Installationspaket aus einem Build-Stamm erstellt.

#!/bin/bash
# TRIMCheck build script
# Copyright Doug Richardson 2015
# Usage: build.sh
#
# The result is a disk image that contains the TRIMCheck installer.
#

DSTROOT=/tmp/trimcheck.dst
SRCROOT=/tmp/trimcheck.src

INSTALLER_PATH=/tmp/trimcheck
INSTALLER_PKG="TRIMCheck.pkg"
INSTALLER="$INSTALLER_PATH/$INSTALLER_PKG"

#
# Clean out anything that doesn't belong.
#
echo Going to clean out build directories
rm -rf build $DSTROOT $SRCROOT $INSTALLER_PATH
echo Build directories cleaned out


#
# Build
#
echo ------------------
echo Installing Sources
echo ------------------
xcodebuild -project TRIMCheck.xcodeproj installsrc SRCROOT=$SRCROOT || exit 1

echo ----------------
echo Building Project
echo ----------------
pushd $SRCROOT
xcodebuild -project TRIMCheck.xcodeproj -target trimcheck -configuration Release install || exit 1
popd

echo ------------------
echo Building Installer
echo ------------------
mkdir -p "$INSTALLER_PATH" || exit 1

echo "Runing pkgbuild. Note you must be connected to Internet for this to work as it"
echo "has to contact a time server in order to generate a trusted timestamp. See"
echo "man pkgbuild for more info under SIGNED PACKAGES."
pkgbuild --identifier "com.delicioussafari.TRIMCheck" \
    --sign "Developer ID Installer: Douglas Richardson (4L84QT8KA9)" \
    --root "$DSTROOT" \
    "$INSTALLER" || exit 1


echo Successfully built TRIMCheck
open "$INSTALLER_PATH"

exit 0
Doug Richardson
quelle
1
Ja, pkgbuild erstellt ein .pkg-Installationsprogramm.
Doug Richardson