准备可用于开发人员ID的macOS安装程序包


188

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

由于使用了Mountain Lion的Gatekeeper,我最终不得不将我的PackageMaker构建脚本带到谷仓后面进行拍摄。PackageMaker已从Xcode中删除,并移至“ Xcode辅助工具”中,因此希望很快将其遗忘。

现在的问题是我怎么用pkgbuildproductbuildpkgutil来代替它?


所以我认为Packagemaker的问题是无法正确签署pkg文件以与Mountain Lion上的网守一起使用?
JasonZ

1
有可能,但是PackageMaker始终是越野车,有地狱,Mac OS X 10.6 Snow Leopard不推荐使用。从长远来看,这将为您节省时间以熟悉新工具。
catlan 2012年

@catlan:您是否有官方链接说packagemaker已于10.6弃用?
卡尔

2
@carleeto:它从未被宣布过时,只是从Xcode中删除,最终像缅甸抗议者一样“消失了”。
错误

5
的Xcode 4.6版本说明:包装机的弃用adcdownload.apple.com/Developer_Tools/xcode_4.6/...
catlan

Answers:


343

我们的示例项目有两个构建目标:HelloWorld.app和Helper.app。我们为每个组件制作一个组件包,并将它们组合到产品档案中

组件包包含净荷由OS X安装进行安装。尽管可以单独安装组件包,但通常将其合并到产品档案中

我们的工具:pkgbuildproductbuild pkgutil

成功完成“构建和存档”后,在终端中打开$ 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,您可以在“ Component Property List”部分中找到值描述。pkgbuild -root生成组件包,如果您不需要更改任何默认属性,则可以省略--component-plist在以下命令中参数。

productbuild --synthesize生成“ 分布定义”

$ 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,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步骤。你也可以使用--sign上paramenter productbuild不是运行的productsign

签署OS X安装程序

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

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

请注意,如果要使用productbuild 创建已签名的产品档案,则没有理由对组件包进行签名。

开发人员证书实用程序

一路:将包复制到Xcode存档中

要将某些内容复制到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

在Xcode 5中,请使用以下值PKG代替:

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

如果您的版本控件未存储Xcode Scheme信息,建议将其作为外壳脚本添加到您的项目中,这样您就可以通过将脚本从工作区拖到后操作中来简单地还原操作。

脚本编写

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

我在WhiteBox中找到了有关Shell脚本的最佳文档-PackageMaker How-to,但请谨慎阅读,因为它是指旧的软件包格式。

附加阅读

已知问题和解决方法

目标选择窗格

仅向用户显示目标选择选项-“为此计算机的所有用户安装”。该选项在视觉上显示为选中状态,但用户需要单击该选项才能继续安装,从而引起混乱。

显示安装程序错误的示例

建议使用Apples文档,<domains enable_anywhere ... />但这会触发新的,多虫的“目标选择窗格”,Apple不在其任何包装中使用。

使用不推荐使用时,<options rootVolumeOnly="true" />将为您提供旧的“目标选择窗格”。 显示旧的目的地选择窗格的示例


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

简短答案:请勿尝试!

长答案:真的吗?不要尝试!阅读安装程序问题和解决方案。读完这篇文章你知道我做了什么吗?我很笨,可以尝试一下。告诉自己,我确定他们已经解决了10.7或10.8中的问题。

首先,我不时看到上述“目标选择窗格错误”。那应该阻止了我,但是我忽略了它。如果您不想在发布软件后的一周内花时间回复支持电子邮件,则必须单击一次漂亮的蓝色选项后再单击,不要使用它。

您现在正在考虑,您的用户足够聪明,可以解决问题,不是吗?好了,这是关于主文件夹安装的另一件事,它们不起作用!

我在大约10台不​​同操作系统版本的机器上进行了两周的测试,但没有失败。所以我发货了。在发布的一个小时内,我向无法安装它的用户表示衷心的感谢。日志提示您将无法解决的权限问题。

因此,让我们再重复一遍:我们不使用Installer进行主文件夹安装!


不接受RTFD进行欢迎,自述,许可和结论productbuild

从开始的RTFD文件开始,安装程序就支持使用漂亮的欢迎屏幕显示图像,但是productbuild不接受它们。

解决方法:使用虚拟rtf文件,并在productbuild完成后将其替换为包中的rtf文件。

注意:您也可以在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

以登录用户而不是安装程序用户身份运行该应用程序很重要。这是通过launchctl asuser uid路径完成的。另外,我们仅在它不是命令行安装程序(通过安装程序工具或Apple Remote Desktop完成)时运行它。



9
这是一个很好的教程,但是假定存在预制的捆绑包。例如,如果要安装一个文件/tmp以便稍后在postflight脚本中进行处理,该如何构造组件列表?所有可用的文档似乎都假定开发人员--analyze至少在最初使用生成了它。
错误

1
如果您不需要更改任何内容,则Component Property List无需运行--analyze。对于后处理文件,我建议将它们全部放入一个pkg中,并将该pkg安装位置设置为/tmp。但也许我误会了你的问题。如果是这样,请在SO上发布更详细的版本。
卡特兰2012年

2
请注意,通过命令行制作软件包以试图逃避打包应用程序中的所有错误绝对没有任何意义。相反,请参见下面有关使用StéphaneSudre的“ Packages”应用程序的评论,该应用程序可以为您解决所有问题!
Bram de Jong

5
@BramdeJong“没有意义”。我不同意。Apple维护命令行工具。Packages是不受社区支持的第三方应用程序,如果Apple做出重大更改,将来可能会中断。对我来说,我宁愿知道命令行技术,这样,如果Apple做出了一些大改变,我就可以继续运行。
Volomike

5
$ pkgbuild --root ./HelloWorld.app是错误的(假设.app是实际的应用程序包)。pkgbuild在目标根目录上运行:即:包含Xcode工具链生成的包的文件夹。因此pkgbuild的参数是我们要打包的包的包含文件夹的路径。未能正确解决此问题,将导致仅包含应用程序内容文件夹的软件包。它不会安装为实际的应用程序捆绑包。赠品在组件plist中。如果其中不包含指定应用程序捆绑包的RootRelativeBundlePath条目,则说明您搞砸了。
乔纳森·米切尔

185

StéphaneSudre有一个非常有趣的应用程序,它可以为您完成所有这些工作,可编写脚本/支持从命令行进行构建,具有非常漂亮的GUI,并且是免费的。可悲的是:它被称为“包裹”,这使得它无法在Google中找到。

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

我希望在开始手工制作自己的脚本之前就已经知道这一点。

套餐应用截图


11
我不敢相信这篇文章没有更多意义。该软件很棒,它支持从命令行构建。
Cesar Mendoza

1
有人尝试使用此工具签署包裹吗?我不能让“设置证书”菜单项来激活....
GTAE86

2
@ user283182:碰到很晚了,可以肯定您已经解决了,但这也许会对其他人有所帮助-我认为您所面临的问题已在[Mac App Store审查指南](developer.apple.com/app-商店/评论/准则/ mac /…),规则2.14:“应用程序必须使用Xcode中包含的Apple打包技术打包和提交-不允许第三方安装程序。”
年长者2014年

4
我希望这是一款付费应用,并且开发人员会不断更新和打补丁。Packages.app非常棒,尤其是如果您想快速部署您的应用程序时。安装总共花了3分钟,以阅读概述,设置我的项目并创建可安装的软件包。斯特凡大赞。
Nikolay Christov 2014年

1
这个程序真棒!
SpencerMüllerDiniz

3

仅供参考,对于那些试图为捆绑包或插件创建软件包安装程序的人,这很容易:

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

2
仅供参考,创建.pkg和创建带有欢迎屏幕,许可证等的真实安装程序是有区别的。
catlan 2015年

是的,我知道,我将其放在此处是因为找不到用于为插件创建pkg安装程序的参考。
gngrwzrd

这让我滚动。仅需最小限度即可接地。
uchuugaka

3

+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上都可以正常工作。


这很好用,但是使用了GOT'CHA:如果先按用户安装,然后按计算机安装,则安装将在用户目录而不是计算机目录中进行,但具有sudo权限。相反,情况并非如此:您可以按机器安装,然后按用户安装,然后在两个地方都安装它。
Terje Dahl

@TerjeDahl是的,这是因为安装后捆绑软件已移动到安装程序先前已安装的捆绑软件ID所在的位置(并且安装程序知道这一点)。我现在不记得的清单文件中的某些设置可以防止这种情况。
PnotNP

@PnotNP啊。如果您还记得的话,愿意返回这些设置,那就太好了!
Terje Dahl

2

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

#!/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
是的,pkgbuild创建一个.pkg安装程序。
道格·理查森
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.