使OS X安装程序包像Pro – Xcode Developer ID ready pkg一样

注意:这仅适用于OS X Installer软件包,提交给Mac App Store的软件包遵循不同的规则。

由于山狮的守门人,我终于不得不把我的PackageMaker构build脚本放在谷仓后面拍摄下来。 PackageMaker已经从Xcode中删除,并进入“辅助工具的Xcode”,所以希望它很快就会被遗忘。

问题是如何使用pkgbuildproductbuildpkgutil来replace它?

我们的示例项目有两个构build目标:HelloWorld.app和Helper.app。 我们为每个产品 打包一个组件包 ,并将其组合成一个产品档案

组件包包含要由OS X安装程序安装的有效内容。 尽pipe组件包可以自行安装,但通常会将其组装到产品档案中

我们的工具: pkgbuild , productbuild和pkgutil

成功的“构build和存档”后,在terminal中打开$ BUILT_PRODUCTS_DIR。

 $ cd ~/Library/Developer/Xcode/DerivedData/.../InstallationBuildProductsLocation $ pkgbuild --analyze --root ./HelloWorld.app HelloWorldAppComponents.plist $ pkgbuild --analyze --root ./Helper.app HelperAppComponents.plist 

这给了我们组件plist,你可以在“组件属性列表”部分find值描述。 pkgbuild -root生成组件包 ,如果不需要更改任何默认属性,则可以在以下命令中省略–component-plist参数。

productbuild –在分配定义中 合成结果。

 $ 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 

我build议看一下iTunes Installers Distribution.xml,看看有什么可能。 您可以使用以下方式提取“安装iTunes.pkg”:

 $ pkgutil --expand "Install iTunes.pkg" "Install iTunes" 

让我们把它放在一起

我通常在我的项目中有一个名为Package的文件夹,其中包括Distribution.xml,组件plists,资源和脚本等。

添加一个名为“生成包”的运行脚本生成阶段 ,该阶段 仅在安装时设置为运行脚本

 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}" 

如果您不必在使用productbuild生成包后更改包,则可以删除pkgutil --expandpkgutil --flatten步骤。 你也可以在productbuild上使用–sign参数而不是运行productsign

签署一个OS X安装程序

软件包使用Developer ID Installer证书进行签名,您可以从Developer Certificate Utility下载。

他们的签名是通过pkgbuildproductbuild或者productsign的 – --sign "Developer ID Installer: John Doe"完成的

请注意,如果要使用productbuild创build已签名的产品归档 ,则没有理由签署组件包

开发者证书工具

一路:将软件包复制到Xcode档案

要将某些内容复制到Xcode存档中,我们不能使用“ 运行脚本构build阶段” 。 为此我们需要使用Schema Action。

编辑架构并展开存档。 然后点击后续操作并添加一个新的运行脚本操作

在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 

在Xcode 5中,用这个值代替PKG

 PKG="${OBJROOT}/ArchiveIntermediates/${TARGET_NAME}/BuildProductsPath/${CONFIGURATION}/${ARCHIVE_FILENAME}" 

如果你的版本控制没有存储Xcode Schema信息,我build议把它作为shell脚本添加到你的项目中,这样你就可以简单地通过将脚本从工作区拖动到操作后来恢复操作。

脚本

有两种不同的脚本: 分布定义文件和Shell脚本中的JavaScript 。

有关Shell脚本的最佳文档,我在WhiteBox – PackageMaker使用指南中find,但谨慎阅读,因为它指的是旧的软件包格式。

额外阅读

  • 扁平封装格式 – 丢失的文档
  • 安装问题和解决scheme
  • 愚蠢的技巧与pkgbuild

已知问题和解决方法

目的地select窗格

用户只有一个select – “为这台计算机的所有用户安装”提供目的地select选项。 该选项出现在视觉上select,但用户需要点击它才能继续进行安装,造成一些混淆。

显示安装程序错误的示例

苹果文档build议使用<domains enable_anywhere ... />但是这会触发新的更多的目的地select窗格,苹果公司在其任何软件包中都不使用。

使用弃用<options rootVolumeOnly="true" />给你旧的目的地select窗格。 显示旧的目标选择窗格的示例


您想要将项目安装到当前用户的主文件夹中。

简短的回答:不要尝试它!

长答案:真的; 不要尝试它! 阅读安装程序问题和解决scheme 。 你知道我读了这个以后做了什么吗? 我很愚蠢的尝试。 告诉我自己,我相信他们在10.7或10.8中解决了这个问题。

首先我不时看到上面提到的Destination Select Pane Bug。 这应该阻止了我,但我忽略了它。 如果你不想花一个星期后,你发布你的软件回答支持电子邮件,他们必须点击一次漂亮的蓝色select不要使用这个。

你现在在想,你的用户很聪明,可以把面板弄出来,不是吗? 那么这里是关于家庭文件夹安装的另一件事,他们不工作!

我在10个不同的操作系统版本的不同的机器上testing了两个星期,而不是什么也没有失败。 所以我发货。 在发布后的一个小时内,我就从那些无法安装的用户那里取回了信息。 日志暗示许可问题,你不能修复。

所以让我们再重复一次:我们不使用安装程序进行家庭文件夹安装!


欢迎,阅读,许可和结论的RTFD不被产品结构接受。

安装程序支持从一开始RTFD文件使图像漂亮的欢迎屏幕,但产品构build不接受它们。

解决方法:使用虚拟rtf文件,并在产品productbuild完成后将其replace到包中。

注意:您也可以在RTFD文件中包含Retina图像。 为此使用多图像tiff文件: tiffutil -cat Welcome.tif Welcome_2x.tif -out FinalWelcome.tif 。 更多细节 。


使用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 

以login用户身份运行应用程序非常重要,而不是以安装程序用户身份运行。 这是通过launchctl asuser uidpath完成的 。 另外,我们只在不是命令行安装的情况下运行它,使用安装程序工具或Apple Remote Desktop完成


StéphaneSudre有一个非常有趣的应用程序,可以为您完成所有这些工作,可以通过命令行编写/支持构build,具有超级好的GUI并且是免费的。 可悲的是:它被称为“包”,这使得它不可能在谷歌find。

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

我希望在我开始手工制作自己的剧本之前就已经知道了。

包应用程序截图

这里是一个构build脚本 ,用于从构build根目录创build一个签名的安装程序包。

 #!/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 

+1接受答案:

安装程序中的目的地select

如果需要在用户域和系统域之间select域(又名目的地),则不要尝试<domains enable_anywhere="true">使用以下内容:

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

enable_currentUserHome~/Applications/下安装应用程序应用程序,并且enable_localSystem允许应用程序安装在/Application

我已经在El Capitan 10.11.6(15G1217)中试过了,它似乎在1个开发机器和2个不同的虚拟机上工作得很好。

仅供参考,对于那些试图为软件包或插件创build软件包安装程序的用户,很容易:

 pkgbuild --component "Color Lists.colorPicker" --install-location ~/Library/ColorPickers ColorLists.pkg 
Interesting Posts