准备好开发人员ID的macOS安装程序包

注意:这仅适用于OS X安装程序包,提交给Mac App Store的包遵循不同的规则。

由于Mountain Lion的看门人,我最终不得不把我的PackageMaker构建脚本放在谷仓后面并拍摄它。PackageMaker已经从Xcode中移除,并移到了“Xcode的辅助工具”中,所以希望它能很快被遗忘。

问题是我如何使用pkgbuildproductbuildpkgutil来替换它?

174519 次浏览

我们的示例项目有两个构建目标:HelloWorld。app和help .app。我们为每个对象创建组件包,并将它们组合成产品档案

组件包包含要由OS X安装程序安装的有效负载。尽管一个组件 包可以单独安装,它通常被合并到产品档案.

. conf中

我们的工具:pkgbuildproductbuildpkgutil

在成功的“建立和存档”之后;在终端打开$BUILT_PRODUCTS_DIR。

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

这给了我们component-plist,你可以在“组件属性列表”;部分找到值描述。pkgbuild根生成组件包,如果你不需要改变任何默认属性,你可以在下面的命令中省略——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

我建议看看iTunes的安装程序 Distribution.xml,看看有什么可能。您可以将“Install iTunes.pkg":

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

让我们把它放在一起

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

添加名为“Generate package”的运行脚本构建阶段,它被设置为仅在安装时运行脚本:

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上使用——标志参数,而不是运行productsign

签署一个OS X安装程序

包使用开发者ID安装程序证书进行签名,您可以从开发人员证书实用程序下载该证书。

它们的签名是用pkgbuildproductbuildproductsign--sign "Developer ID Installer: John Doe"参数完成的。

注意,如果你打算使用productbuild创建一个有符号的产品档案,没有理由对组件包进行签名。

Developer Certificate Utility

复制包到Xcode存档

要将内容复制到Xcode Archive中,我们不能使用运行脚本构建阶段。为此,我们需要使用Scheme Action。

编辑方案并展开存档。然后单击post-actions并添加新建运行脚本动作:

在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 Scheme信息,我建议将它作为shell脚本添加到你的项目中,这样你就可以通过将脚本从工作空间拖到post-action中来简单地恢复操作。

脚本

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

我在WhiteBox - PackageMaker指南中找到了关于Shell脚本的最佳文档,但请谨慎阅读,因为它引用了旧的包格式。

苹果硅

为了让这个包作为arm64运行,分发文件必须在它的hostArchitectures部分中指定它支持arm64x86_64:

<options hostArchitectures="arm64,x86_64" />

更多的阅读

已知问题和解决方法

目标选择窗格

用户看到的目标选择选项只有一个选项——“为这台计算机的所有用户安装”。该选项在视觉上显示为已选中,但用户需要单击它才能继续安装,这造成了一些混乱。

安装程序错误示例 . exe . exe . exe

苹果文档建议使用<domains enable_anywhere ... />,但这会触发新的更多错误的目的地选择窗格,苹果不会在任何他们的包中使用。

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

.

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

简单的回答:不要尝试!

长回答:真正的;不要尝试!读取安装问题及解决方案。你知道我看完之后做了什么吗?我蠢到去尝试了。告诉自己,我确信他们在10.7或10.8中修复了这些问题。

首先,我不时看到上面提到的目标选择窗格错误。这应该阻止我,但我没有理会。如果你不想在发布软件后花一周的时间回复支持邮件,他们必须点击一次漂亮的蓝色选择,不要使用这个。

您现在认为您的用户足够聪明,能够理解面板,是吗?这里是关于主文件夹安装的另一件事,它们不起作用!

我在大约10台不同的机器上用不同的操作系统版本测试了两周,它从未失败过。所以我把它发了出去。发布后不到一个小时,我就收到了无法安装的用户反馈。日志提示了您无法修复的权限问题。

所以让我们再重复一遍:我们不使用安装程序来安装主文件夹!


productbuild不接受Welcome、Read-me、License和Conclusion的RTFD。

安装程序从一开始就支持RTFD文件来制作带有图像的漂亮的欢迎屏幕,但productbuild不接受它们。

< p >解决方案: 使用一个虚拟的rtf文件,并在productbuild完成后将其替换到包中

注意:你也可以在RTFD文件中有视网膜图像。使用多图像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

重要的是运行应用程序作为登录用户,而不是作为安装用户。这是通过Launchctl asuser uid路径完成的。此外,我们只在不是命令行安装时运行它,使用安装程序工具或苹果远程桌面完成。


Stéphane Sudre有一个非常有趣的应用程序,它为你做了所有这些,是可脚本化的/支持从命令行构建,有一个超级好的GUI,是免费的。可悲的是:它被称为“包”,这使得它不可能在谷歌中找到。

http://s.sudre.free.fr/Software/Packages/about.html < a href = " http://s.sudre.free.fr/Software/Packages/about.html " > < / >

我希望我在开始手工编写自己的脚本之前就知道这一点。

Packages application截图

下面是一个构建脚本,它从构建根目录中创建一个签名安装包。

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

对于那些试图为包或插件创建包安装程序的人来说,这很简单:

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

A +1接受答案:

安装程序中的目标选择

如果希望在用户域和系统域之间选择域(也就是目的地),那么不要尝试<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个不同的vm中工作得很好。