Nuestro proyecto de ejemplo tiene dos objetivos de compilación: HelloWorld.app y Helper.app. Creamos un paquete de componentes para cada uno y los combinamos en un archivo de producto .
Un paquete de componentes contiene carga útil que debe instalar el instalador de OS X. Aunque un paquete de componentes se puede instalar solo, normalmente se incorpora a un archivo de producto .
Después de un exitoso "Build and Archive", abra $ BUILT_PRODUCTS_DIR en la Terminal.
$ cd ~/Library/Developer/Xcode/DerivedData/.../InstallationBuildProductsLocation
$ pkgbuild --analyze --root ./HelloWorld.app HelloWorldAppComponents.plist
$ pkgbuild --analyze --root ./Helper.app HelperAppComponents.plist
Esto nos da la lista de componentes, puede encontrar la descripción del valor en la sección "Lista de propiedades de componentes" . pkgbuild -root genera el paquetes de componentes , si no necesita cambiar ninguna de las propiedades predeterminadas, puede omitir el parámetro --component-plist en el siguiente comando.
productbuild: sintetizar resultados en una definición de distribución .
$ 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
En Distribution.xml puede cambiar cosas como título, fondo, bienvenida, archivo Léame, licencia, etc. Convierte tus paquetes de componentes y la definición de distribución con este comando en un archivo de producto :
$ productbuild --distribution ./Distribution.xml \
--package-path . \
./Installer.pkg
Recomiendo echar un vistazo a iTunes Installers Distribution.xml para ver qué es posible. Puede extraer "Instalar iTunes.pkg" con:
$ pkgutil --expand "Install iTunes.pkg" "Install iTunes"
Vamos a armarlo
Por lo general, tengo una carpeta llamada Paquete en mi proyecto que incluye elementos como Distribution.xml, componentes-listas, recursos y scripts.
Agregue una Fase de compilación de script de ejecución denominada "Generar paquete", que se establece en Ejecutar script solo al instalar :
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}"
Si no tiene que cambiar el paquete después de que se genera con productbuild , puede deshacerse de los pasos pkgutil --expand
y pkgutil --flatten
. También se puede utilizar el --sign paramenter en productbuild en lugar de correr productsign .
Firmar un instalador de OS X
Los paquetes se firman con el certificado de instalador de ID de desarrollador que puede descargar desde Developer Certificate Utility .
La firma se realiza con el --sign "Developer ID Installer: John Doe"
parámetro pkgbuild , productbuild o productsign .
Tenga en cuenta que si va a crear un archivo de producto firmado usando productbuild, no hay razón para firmar los paquetes de componentes .
Todo el camino: Copie el paquete en Xcode Archive
Para copiar algo en el Archivo Xcode no podemos usar la Fase de Ejecución de Script Script . Para esto necesitamos usar una acción de esquema.
Editar esquema y expandir archivo. A continuación, haga clic en acciones posteriores y agregue una nueva acción Ejecutar script :
En 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
En Xcode 5, use este valor para PKG
:
PKG="${OBJROOT}/ArchiveIntermediates/${TARGET_NAME}/BuildProductsPath/${CONFIGURATION}/${ARCHIVE_FILENAME}"
En caso de que su control de versiones no almacene la información del Esquema Xcode, sugiero agregar esto como script de shell a su proyecto para que pueda restaurar la acción simplemente arrastrando el script desde el espacio de trabajo a la acción posterior.
Scripting
Hay dos tipos diferentes de secuencias de comandos: JavaScript en los archivos de definición de distribución y las secuencias de comandos de Shell.
La mejor documentación sobre los scripts de Shell la encontré en WhiteBox - PackageMaker How-to , pero lea esto con precaución porque se refiere al formato de paquete anterior.
Lectura adicional
Problemas conocidos y soluciones alternativas
Panel de selección de destino
Al usuario se le presenta la opción de selección de destino con una sola opción: "Instalar para todos los usuarios de esta computadora". La opción aparece seleccionada visualmente, pero el usuario debe hacer clic en ella para continuar con la instalación, lo que genera cierta confusión.
Apples Documentation recomienda usar, <domains enable_anywhere ... />
pero esto desencadena el nuevo panel de selección de destino con más errores que Apple no usa en ninguno de sus paquetes.
El uso de la opción obsoleta le <options rootVolumeOnly="true" />
proporciona el Panel de selección de destino anterior.
Desea instalar elementos en la carpeta de inicio del usuario actual.
Respuesta corta: ¡NO LO INTENTES!
Respuesta larga: REALMENTE; ¡NO LO INTENTES! Leer problemas y soluciones del instalador . ¿Sabes lo que hice incluso después de leer esto? Fui lo suficientemente estúpido como para intentarlo. Diciéndome a mí mismo que estoy seguro de que solucionaron los problemas en 10.7 o 10.8.
En primer lugar, vi de vez en cuando el error de panel de selección de destino mencionado anteriormente. Eso debería haberme detenido, pero lo ignoré. Si no desea pasar la semana posterior a la publicación de su software respondiendo correos electrónicos de soporte que deben hacer clic una vez que la buena selección azul NO use esto.
Ahora está pensando que sus usuarios son lo suficientemente inteligentes como para descubrir el panel, ¿no es así? Bueno, aquí hay otra cosa sobre la instalación de la carpeta de inicio, ¡NO FUNCIONAN!
Lo probé durante dos semanas en alrededor de 10 máquinas diferentes con diferentes versiones de sistema operativo y lo que no, y nunca falló. Entonces lo envié. Una hora después del lanzamiento, me alegro de los usuarios que simplemente no pudieron instalarlo. Los registros insinuaron problemas de permisos que no podrá solucionar.
Así que repitámoslo una vez más: ¡No utilizamos el instalador para las instalaciones de la carpeta de inicio!
RTFD para Bienvenido, Léame, Licencia y Conclusión no es aceptado por productbuild
.
El instalador admite desde el principio los archivos RTFD para crear bonitas pantallas de bienvenida con imágenes, pero productbuild no las acepta.
Soluciones: use un archivo ficticio rtf y reemplácelo en el paquete una vez que productbuild
haya terminado.
Nota: También puede tener imágenes Retina dentro del archivo RTFD. Utilizar archivos TIFF multi-imagen para esto: tiffutil -cat Welcome.tif Welcome_2x.tif -out FinalWelcome.tif
. Más detalles .
Iniciar una aplicación cuando la instalación se realiza con un script BundlePostInstallScriptPath :
#!/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 importante ejecutar la aplicación como usuario conectado, no como usuario instalador. Esto se hace con launchctl asuser uid path . Además, solo lo ejecutamos cuando no es una instalación de línea de comandos, realizada con la herramienta de instalación o Apple Remote Desktop .