注意:此处仅涉及适用于 OS X Installer 软件包,提交至 Mac App Store 的软件包需要遵循不同的规则。
由于 Mountain Lion 的 Gatekeeper 功能,我最终只好把我的 PackageMaker 构建脚本送到了垃圾堆。PackageMaker 已经从 Xcode 中删除并移到“Xcode 辅助工具”中,所以希望它会被很快忘记。
问题是如何使用 pkgbuild
、productbuild
和 pkgutil
来替代它?
注意:此处仅涉及适用于 OS X Installer 软件包,提交至 Mac App Store 的软件包需要遵循不同的规则。
由于 Mountain Lion 的 Gatekeeper 功能,我最终只好把我的 PackageMaker 构建脚本送到了垃圾堆。PackageMaker 已经从 Xcode 中删除并移到“Xcode 辅助工具”中,所以希望它会被很快忘记。
问题是如何使用 pkgbuild
、productbuild
和 pkgutil
来替代它?
$ cd ~/Library/Developer/Xcode/DerivedData/.../InstallationBuildProductsLocation
$ pkgbuild --analyze --root ./HelloWorld.app HelloWorldAppComponents.plist
$ pkgbuild --analyze --root ./Helper.app HelperAppComponents.plist
$ 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
在Distribution.xml中,您可以更改标题、背景、欢迎页面、自述文件、许可证等内容。使用此命令,您可以将组件包和分发定义转换为产品存档。
$ productbuild --distribution ./Distribution.xml \
--package-path . \
./Installer.pkg
我建议查看iTunes Installers Distribution.xml,了解可能的情况。您可以使用以下命令提取“Install iTunes.pkg”:
$ pkgutil --expand "Install iTunes.pkg" "Install iTunes"
我通常在我的项目中有一个名为Package的文件夹,其中包括Distribution.xml、组件清单、资源和脚本等内容。
添加一个名为“生成软件包”的运行脚本构建阶段,并将其设置为仅在安装时运行脚本:
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}"
pkgutil --expand
和pkgutil --flatten
步骤。此外,您可以在productbuild上使用--sign参数,而不是运行productsign。
包使用Developer ID Installer证书进行签名,您可以从Developer Certificate Utility下载该证书。
签名是通过pkgbuild、productbuild或productsign的--sign "Developer ID Installer: John Doe"
参数完成的。
请注意,如果要使用productbuild创建已签名的产品归档文件,则没有必要对组件包进行签名。
要将某个东西复制到Xcode存档中,我们不能使用“运行脚本构建阶段”。这时,我们需要使用方案操作。
编辑方案并展开存档。然后单击“后操作”并添加一个“新的运行脚本操作”:
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
PKG
:PKG="${OBJROOT}/ArchiveIntermediates/${TARGET_NAME}/BuildProductsPath/${CONFIGURATION}/${ARCHIVE_FILENAME}"
hostArchitectures
部分中指定它支持arm64
,除了x86_64
。<options hostArchitectures="arm64,x86_64" />
目标选择窗格
用户只能看到一个选项 - "为此计算机上的所有用户安装"。该选项在视觉上被选中,但用户需要单击它才能继续安装,这可能会引起一些混淆。
Apples Documentation建议使用<domains enable_anywhere ... />
,但这会触发新的更多错误的目标选择窗格,而苹果在任何包中都不使用它。<options rootVolumeOnly="true" />
可以让您获得旧的目标选择窗格。
我在大约10台不同的机器上测试了两周,涉及不同的操作系统版本等内容,从未出现失败情况。因此我发布了它。但是,在发布后一个小时内,我收到了用户的反馈,他们无法安装它。日志提示存在权限问题,你无法解决。
所以让我们再重复一遍:我们不使用安装程序进行家目录安装!
欢迎、自述、许可证和结论的 RTFD 文件不被 productbuild
接受。
从一开始,安装程序支持使用 RTFD 文件制作带有图像的漂亮欢迎屏幕,但是 productbuild 不接受它们。
解决方法:
使用虚拟 rtf 文件,并在 productbuild
完成后替换包中的文件。
注意:您还可以在 RTFD 文件中使用 Retina 图像。为此,请使用多图像 tiff 文件:tiffutil -cat Welcome.tif Welcome_2x.tif -out FinalWelcome.tif
。更多 details。
使用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
/tmp
,以便在后续处理脚本中使用,我该如何构建组件列表?所有可用文档似乎都假定开发人员最初使用--analyze
生成它。 - bug有一个非常有趣的应用程序,由Stéphane Sudre开发,可以为您完成所有这些工作,支持脚本编写/命令行构建,具有超级漂亮的GUI界面,而且还是免费的。可悲的是:它被称为“Packages”,在谷歌上几乎无法搜索到。
http://s.sudre.free.fr/Software/Packages/about.html
我希望在我开始手工编写我的脚本之前就已经知道了它。
如果你想为一个bundle或插件创建一个软件包安装程序,那很容易:
pkgbuild --component "Color Lists.colorPicker" --install-location ~/Library/ColorPickers ColorLists.pkg
对于已接受答案的+1:
安装程序中的目标选择
如果需要在用户域和系统域之间进行域(即目标)选择,那么不要尝试<domains enable_anywhere="true">
,而应该使用以下代码:
<domains enable_currentUserHome="true" enable_localSystem="true"/>
enable_currentUserHome会将应用程序安装在~/Applications/
中,而enable_localSystem
则允许将应用程序安装在/Application
下。
我已在El Capitan 10.11.6(15G1217)中尝试过此方法,并且在一台开发机器和两个不同的虚拟机上都运行得非常完美。
点击此处查看脚本。
#!/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
pbxbuild
和projectbuild
逐渐替代(尤其是针对 App Store),这也许是最好的选择,但说它已经死亡并不真实。(“我还没有死!”) - Quinn Taylor