xcode 制作准备好开发人员 ID 的 macOS 安装程序包
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11487596/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Making macOS Installer Packages which are Developer ID ready
提问by catlan
Note: This is for OS X Installerpackages only, packages for submission to the Mac App Storefollow different rules.
注意:这仅适用于OS X 安装程序包,提交到Mac App Store 的包遵循不同的规则。
Because of Mountain Lion's GatekeeperI finally had to take my PackageMakerbuild script behind the barn and shoot it. PackageMaker was already removed from Xcode and moved into "Auxiliary Tools for Xcode", so hopefully it will be soon forgotten.
由于 Mountain Lion 的守门人,我最终不得不将我的PackageMaker构建脚本带到谷仓后面并拍摄。PackageMaker 已经从 Xcode 中删除并移到“Xcode 的辅助工具”中,所以希望它很快就会被遗忘。
The question is how do I use pkgbuild
, productbuild
, and pkgutil
to replace it?
现在的问题是我怎么用pkgbuild
,productbuild
和pkgutil
来代替它?
回答by catlan
Our example project has two build targets: HelloWorld.app and Helper.app. We make a component packagefor each and combine them into a product archive.
我们的示例项目有两个构建目标:HelloWorld.app 和 Helper.app。我们为每个组件制作一个组件包并将它们组合成一个产品档案。
A component packagecontains payload to be installed by the OS X Installer. Although a component package can be installed on its own, it is typically incorporated into a product archive.
甲组件包包含净荷由OS X安装进行安装。虽然组件包可以单独安装,但它通常被合并到产品档案中。
Our tools: pkgbuild, productbuild, and pkgutil
我们的工具:pkgbuild、productbuild和pkgutil
After a successful "Build and Archive" open $BUILT_PRODUCTS_DIR in the Terminal.
成功“构建和存档”后,在终端中打开 $BUILT_PRODUCTS_DIR。
$ cd ~/Library/Developer/Xcode/DerivedData/.../InstallationBuildProductsLocation
$ pkgbuild --analyze --root ./HelloWorld.app HelloWorldAppComponents.plist
$ pkgbuild --analyze --root ./Helper.app HelperAppComponents.plist
This give us the component-plist, you find the value description in the "Component Property List"section. pkgbuild -rootgenerates the component packages, if you don't need to change any of the default properties you can omit the --component-plistparameter in the following command.
这为我们提供了组件 plist,您可以在“组件属性列表”部分找到值描述。pkgbuild -root生成组件包,如果您不需要更改任何默认属性,则可以省略以下命令中的--component-plist参数。
productbuild --synthesizeresults in a Distribution Definition.
productbuild --synthesize结果在Distribution Definition。
$ 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
In the Distribution.xmlyou can change things like title, background, welcome, readme, license, and so on. You turn your component packagesand distribution definition with this command into a product archive:
在Distribution.xml 中,您可以更改标题、背景、欢迎、自述、许可证等内容。您可以使用此命令将组件包和分发定义转换为产品档案:
$ productbuild --distribution ./Distribution.xml \
--package-path . \
./Installer.pkg
I recommend to take a look at iTunes InstallersDistribution.xml to see what is possible. You can extract "Install iTunes.pkg" with:
我建议查看iTunes InstallersDistribution.xml 以了解可能的情况。您可以使用以下命令提取“安装 iTunes.pkg”:
$ pkgutil --expand "Install iTunes.pkg" "Install iTunes"
Lets put it together
让我们把它放在一起
I usually have a folder named Package in my project which includes things like Distribution.xml, component-plists, resources and scripts.
我的项目中通常有一个名为 Package 的文件夹,其中包含 Distribution.xml、component-plist、资源和脚本等内容。
Add a Run Script Build Phasenamed "Generate Package", which is set to Run script only when installing:
添加一个名为“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}"
If you don't have to change the package after it's generated with productbuildyou could get rid of the pkgutil --expand
and pkgutil --flatten
steps. Also you could use the --signparamenter on productbuildinstead of running productsign.
如果在使用productbuild生成包后不必更改包,则可以去掉pkgutil --expand
和pkgutil --flatten
步骤。您也可以在productbuild上使用--sign参数而不是运行productsign。
Sign an OS X Installer
签署 OS X 安装程序
Packages are signed with the Developer ID Installercertificate which you can download from Developer Certificate Utility.
软件包使用Developer ID 安装程序证书签名,您可以从Developer Certificate Utility下载该证书。
They signing is done with the --sign "Developer ID Installer: John Doe"
parameter of pkgbuild, productbuildor productsign.
他们的签名是使用pkgbuild、productbuild或productsign的--sign "Developer ID Installer: John Doe"
参数完成的。
Note that if you are going to create a signed product archiveusing productbuild, there is no reason to sign the component packages.
请注意,如果您要使用 productbuild创建签名的产品存档,则没有理由对组件包进行签名。
All the way: Copy Package into Xcode Archive
一路:将包复制到Xcode存档中
To copy something into the Xcode Archive we can't use the Run Script Build Phase. For this we need to use a Scheme Action.
要将某些内容复制到 Xcode Archive,我们不能使用Run Script Build Phase。为此,我们需要使用 Scheme Action。
Edit Scheme and expand Archive. Then click post-actions and add a New Run Script Action:
编辑方案并展开存档。然后点击 post-actions 并添加一个New Run Script Action:
In Xcode 6:
在 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
In Xcode 5, use this value for PKG
instead:
在 Xcode 5 中,使用此值PKG
代替:
PKG="${OBJROOT}/ArchiveIntermediates/${TARGET_NAME}/BuildProductsPath/${CONFIGURATION}/${ARCHIVE_FILENAME}"
In case your version control doesn't store Xcode Scheme information I suggest to add this as shell script to your project so you can simple restore the action by dragging the script from the workspace into the post-action.
如果您的版本控制不存储 Xcode Scheme 信息,我建议将其作为 shell 脚本添加到您的项目中,以便您可以通过将脚本从工作区拖到后期操作中来简单地恢复操作。
Scripting
脚本编写
There are two different kinds of scripting: JavaScript in Distribution Definition Filesand Shell Scripts.
有两种不同类型的脚本:分发定义文件中的 JavaScript和 Shell 脚本。
The best documentation about Shell Scripts I found in WhiteBox - PackageMaker How-to, but read this with caution because it refers to the old package format.
我在WhiteBox - PackageMaker How-to 中找到的有关 Shell 脚本的最佳文档,但请谨慎阅读,因为它指的是旧的包格式。
Additional Reading
补充阅读
- Flat Package Format - The missing documentation
- Installer Problems and Solutions
- Stupid tricks with pkgbuild
- persisting obsolescence
Known Issues and Workarounds
已知问题和解决方法
Destination Select Pane
目的地选择窗格
The user is presented with the destination select option with only a single choice - "Install for all users of this computer". The option appears visually selected, but the user needs to click on it in order to proceed with the installation, causing some confusion.
用户将看到目标选择选项,只有一个选择 - “为此计算机的所有用户安装”。该选项在视觉上显示为选中状态,但用户需要单击它才能继续安装,这会造成一些混乱。
Apples Documentation recommends to use <domains enable_anywhere ... />
but this triggers the new more buggy Destination Select Pane which Apple doesn't use in any of their Packages.
Apples 文档建议使用,<domains enable_anywhere ... />
但这会触发新的更多错误的 Destination Select Pane,Apple 不会在其任何包中使用该窗格。
Using the deprecate <options rootVolumeOnly="true" />
give you the old Destination Select Pane.
使用 deprecate<options rootVolumeOnly="true" />
为您提供旧的 Destination Select Pane。
You want to install items into the current user's home folder.
您想将项目安装到当前用户的主文件夹中。
Short answer: DO NOT TRY IT!
简短回答:不要尝试!
Long answer: REALLY; DO NOT TRY IT! Read Installer Problems and Solutions. You know what I did even after reading this? I was stupid enough to try it. Telling myself I'm sure that they fixed the issues in 10.7 or 10.8.
长答案:真的;不要尝试!阅读安装程序问题和解决方案。你知道我在读完这篇文章后做了什么吗?我笨到试一试。告诉自己我确信他们在 10.7 或 10.8 中解决了这些问题。
First of all I saw from time to time the above mentioned Destination Select Pane Bug. That should have stopped me, but I ignored it. If you don't want to spend the week after you released your software answering support e-mails that they have to click once the nice blue selection DO NOT use this.
首先,我不时看到上面提到的 Destination Select Pane Bug。那应该阻止我,但我忽略了它。如果您不想在发布软件后的一周内回复支持电子邮件,他们必须在漂亮的蓝色选项中点击一次,请不要使用它。
You are now thinking that your users are smart enough to figure the panel out, aren't you? Well here is another thing about home folder installation, THEY DON'T WORK!
您现在认为您的用户足够聪明,可以找出面板,不是吗?好吧,这是关于主文件夹安装的另一件事,它们不起作用!
I tested it for two weeks on around 10 different machines with different OS versions and what not, and it never failed. So I shipped it. Within an hour of the release I heart back from users who just couldn't install it. The logs hinted to permission issues you are not gonna be able to fix.
我在大约 10 台具有不同操作系统版本的不同机器上对其进行了两周的测试,并且从未失败过。所以我发货了。在发布后的一个小时内,我收到了无法安装它的用户的回复。日志暗示了您无法修复的权限问题。
So let's repeat it one more time: We do not use the Installer for home folder installations!
所以让我们再重复一遍:我们不使用安装程序来安装主文件夹!
RTFD for Welcome, Read-me, License and Conclusion is not accepted by productbuild
.
用于欢迎、自述、许可和结论的 RTFD 不被productbuild
.
Installer supported since the beginning RTFD files to make pretty Welcome screens with images, but productbuild doesn't accept them.
安装程序从一开始就支持 RTFD 文件以制作带有图像的漂亮欢迎屏幕,但 productbuild 不接受它们。
Workarounds:
Use a dummy rtf file and replace it in the package by after productbuild
is done.
解决方法:使用虚拟 rtf 文件并在productbuild
完成后替换包中的它。
Note: You can also have Retina images inside the RTFD file. Use multi-image tiff files for this: tiffutil -cat Welcome.tif Welcome_2x.tif -out FinalWelcome.tif
. More details.
注意:您还可以在 RTFD 文件中包含 Retina 图像。为此使用多图像 tiff 文件:tiffutil -cat Welcome.tif Welcome_2x.tif -out FinalWelcome.tif
. 更多详情。
Starting an application when the installation is done with a BundlePostInstallScriptPathscript:
使用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
It is important to run the app as logged in user, not as the installer user. This is done with launchctl asuser uid path. Also we only run it when it is not a command line installation, done with installertool or Apple Remote Desktop.
以登录用户的身份运行应用程序很重要,而不是安装程序用户。这是通过launchctl asuser uid path 完成的。此外,我们仅在它不是命令行安装时才运行它,使用安装程序工具或Apple Remote Desktop 完成。
回答by Bram de Jong
There is one very interesting application by Stéphane Sudre which does all of this for you, is scriptable / supports building from the command line, has a super nice GUI and is FREE. Sad thing is: it's called "Packages" which makes it impossible to find in google.
Stéphane Sudre 有一个非常有趣的应用程序,它可以为您完成所有这些工作,可编写脚本/支持从命令行构建,具有超级漂亮的 GUI 并且是免费的。可悲的是:它被称为“包”,这使得它无法在谷歌中找到。
http://s.sudre.free.fr/Software/Packages/about.html
http://s.sudre.free.fr/Software/Packages/about.html
I wished I had known about it before I started handcrafting my own scripts.
我希望在我开始手工编写自己的脚本之前就知道它。
回答by gngrwzrd
FYI for those that are trying to create a package installer for a bundle or plugin, it's easy:
对于那些试图为包或插件创建包安装程序的人来说,这很简单:
pkgbuild --component "Color Lists.colorPicker" --install-location ~/Library/ColorPickers ColorLists.pkg
回答by PnotNP
A +1 to accepted answer:
接受答案的+1:
Destination Selection in Installer
安装程序中的目的地选择
If domain (a.k.a destination) selection is desired between user domain and system domain then rather than trying <domains enable_anywhere="true">
use following:
如果在用户域和系统域之间需要域(又名目标)选择,则不要尝试<domains enable_anywhere="true">
使用以下内容:
<domains enable_currentUserHome="true" enable_localSystem="true"/>
<domains enable_currentUserHome="true" enable_localSystem="true"/>
enable_currentUserHomeinstalls application app under ~/Applications/
and enable_localSystem
allows the application to be installed under /Application
enable_currentUserHome 下安装应用程序~/Applications/
并enable_localSystem
允许应用程序安装在/Application
I've tried this in El Capitan 10.11.6 (15G1217) and it seems to be working perfectly fine in 1 dev machine and 2 different VMs I tried.
我已经在 El Capitan 10.11.6 (15G1217) 中尝试过这个,它似乎在我尝试过的 1 台开发机器和 2 台不同的 VM 中工作得很好。
回答by Doug Richardson
Here is a build scriptwhich creates a signed installer package out of a build root.
这是一个构建脚本,它从构建根目录中创建一个签名的安装程序包。
#!/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