In my book “Packaging for Apple Administrators” I show a great use of pkgbuild
to wrap an application in a package installer:
$ pkgbuild --component /Applications/Numbers.app Numbers.pkg
If the application is not already in the /Applications
folder, you have to add the --install-location
:
$ pkgbuild --component /Volumes/Firefox/Firefox.app --install-location /Applications Firefox.pkg
This is great and wonderful, but has one drawback: the installers pkgbuild
creates this way are ‘relocatable’. When the installer does not find the application in the target location, it will look if the application is installed elsewhere on the system. If it finds the ‘relocated’ application bundle, it will happily try to update it in the new location.
Usually this is not a big problem on managed systems. However, if users have copies of applications in unusual locations, e.g., because they do not have permission to install in /Applications
or because they themselves are admins with dozens of versions in ~/Library/AutoPkg
, then this can lead to unexpected behavior or failure.
The common solution to this is to create ‘non-relocatable’ installer packages.
What makes a pkg relocatable
The relocate
element in the PackageInfo
file in an installer package controls this behavior. You can see the PackageInfo
file in Pacifist or with pkgutil
:
$ pkgutil --expand Firefox.pkg Firefox_expanded
$ more Firefox_expanded/PackageInfo
Among much other data you will see this xml element:
<relocate>
<bundle id="org.mozilla.firefox"/>
</relocate>
This tells the Installer to look for an application bundle with the given identifier and install in that location. To disable this, you can replace the above element with an empty relocate
element:
<relocate/>
Then installer will install or upgrade in the given install-location (e.g. /Applications
) only.
You can apply this change to the expanded PackageInfo file with a text editor and re-create the pkg file with
$ pkgutil --flatten Firefox_expanded/ Firefox-nr.pkg
(‘nr’ for ‘non-relocatable’)
However, applying these steps after creating each package is tedious and error-prone, so we want to look for a better solution.
Telling pkgbuild
to not re-locate
The pkgbuild
man page mentions there is an option to create non-relocatable installer pkgs with the BundleIsRelocatable
option in a ‘component property list’. This is great, since it is better to use documented options, rather than hacking the PackageInfo
directly. However, to use the --component-plist
option with pkgbuild
you have to use the --root
option rather than the --component
option This requires a bit more effort.
First create a project folder:
$ mkdir -p Firefox/payload
$ cd Firefox
And copy the application to the payload
directory:
$ cp -R /Volumes/Firefox/Firefox.app payload/
Then you can use pkgbuild
’s --analyze
to create a template component property list:
$ pkgbuild --analyze --root payload Firefox-component.plist
pkgbuild: Inferring bundle components from contents of payload
pkgbuild: Writing new component property list to Firefox-component.plist
You can then open the generated property list file in a text or property list editor. You will see several values for different settings and a list of ChildBundles
. Change the value of the BundleIsRelocatable
key from <true/>
to <false/>
. You can do this in the editor or with the plutil
command:
$ plutil -replace BundleIsRelocatable -bool NO Firefox-component.plist
Then build the package with pkgbuild
:
$ pkgbuild --root payload --identifier org.mozilla.firefox --version 53.0.3 --install-location /Applications --component-plist Firefox-component.plist Firefox-53.0.3.pkg
This will build the package installer with an empty relocate
element.
Note: munki-pkg
has an option suppress-bundle-relocation
which achieves the same result.
QuickPkg
This approach can be useful but is still complicated. To simplify the creation I have updated my quickpkg
tool to create non-relocatable packages by default. You can change the new default behavior with the --relocatable
option.
$ quickpkg ~/Downloads/Firefox\ 53.0.3.dmg
Firefox-53.0.3.pkg
Enjoy!
Does your book have examples on how to do the followings:
1. Create a package that contains payload files plus adding a .app application.
2 / Create a package that contains a payload files plus packages to be installed from a shell script.
1. Yes, the book does so with the example of BBEdit
2. Not directly. But, the book explains how to build pkgs with a payload, how to install other pkgs or installers with a postinstall script, and how to combine pkgs.