With bundle packages, you can use the following scripts:
Script Name | Run If | Run When |
---|---|---|
preflight | Run every time. | Run before the payload has been installed and before the other scripts. |
preinstall | Run if the package is installed for the first time on the system. | Run before the payload is installed. |
preupgrade | Run if the package has already been installed on the system. | Run before the payload is installed. |
postinstall | Run if the package is installed for the first time on the system. | Run after the payload has been installed. |
preupgrade | Run if the package has already been installed on the system. | Run after the payload has been installed. |
postflight | Run every time. | Run after the payload has been installed and after the other scripts. |
With flat packages, you can only use the following scripts:
Script Name | Run if | Run when |
---|---|---|
preinstall | Run every time. | Run before the payload has been installed. |
postinstall | Run every time. | Run after the payload has been installed. |
So, basically, the preflight and postflight script have respectively become the preinstall and postinstall scripts. And there's apparently no way to run a specific script depending on the fact the payload is being installed for the first time or being upgraded.
Actually, there is a way. It just requires us to detect ourself when the package is being installed for the first time or being upgraded. And then we can decide to launch a specific upgrade or install script from the preinstall or postinstall script of the flat package.
Figuring out whether a package has already been installed or notNote: To avoid confusions between the flat package and bundle package preinstall/postinstall scripts, we will use pre-installation and post-installation to describe the preinstall and postinstall scripts for flat package.
Apple provides on every system a command line tool named pkgutil(1)
which shall be used when you want to get information about packages in the receipts data base.
The --pkgs
option of this tool lists all the packages whose identifier matches a regular expression:
The --volume
option of this tool lets us specify which volume should be inspected.
/usr/sbin/pkgutil --volume / --pkgs=com.mycompany.pkg.mypackage
If the flat package has not been installed yet on the startup volume, the output will be empty.
Writing custom pre-installation and post-installation scriptsSo we can use the solution described above in the pre-installation script of a flat package to either run a preinstall or preupgrade script as we could with bundle packages.
Note: For the --volume
argument, we will use the fact that the target volume of an installation is passed as $3 (which we will surround with double-quotes to deal with volumes that have spaces in their names).
#!/bin/sh
foundPackage=`/usr/sbin/pkgutil --volume "$3" --pkgs=com.mycompany.pkg.mypackage`
if test -n "$foundPackage"; then
# It's an upgrade
/bin/sh ./preupgrade.sh
else
# It's a clean install
/bin/sh ./preinstall.sh
fi
exit 0
#!/bin/sh
foundPackage=`/usr/sbin/pkgutil --volume "$3" --pkgs=com.mycompany.pkg.mypackage`
if test -n "$foundPackage"; then
# It's an upgrade
/bin/sh ./postupgrade.sh
else
# It's a clean install
/bin/sh ./postinstall.sh
fi
exit 0
Then we just need to add the preinstall.sh, preupgrade.sh, postinstalll.sh, postupgrade.sh scripts to the Resources of the flat package.
Mission accomplished.
Improving the scriptsWell, the mission is not completely accomplished.
There are 2 issues with this solution:
This is a small one. If we want to re-use this solution in other packages or we need to change the identifier of the package for whatever reason, we will need to edit the pre-installation.sh and post-installation.sh scripts too.
There's a solution for that. When the pre-installation and post-installation scripts are run by the installation engine, some environment variables specifically related to the installation process are set. In this case, we are glad to notice that the INSTALL_PKG_SESSION_ID
environment variable is set to the identifier of the flat package being currently installed.
So, we just need to replace com.mycompany.pkg.mypackage in the pre-installation.sh and post-installation.sh scripts with $INSTALL_PKG_SESSION_ID
.
This is a major one when your package needs to work on Mac OS X v10.5.x.
Let's say you are installing your package for the first time and you have used the solution described above.
Here's what happens when you install your package on Mac OS X v10.6 or later:
/usr/sbin/pkgutil --pkgs=com.mycompany.pkg.mypackage
command is invoked from within the pre-installation.sh script and it returns nothing. So you can decide that this is a clean install/usr/sbin/pkgutil --pkgs=com.mycompany.pkg.mypackage
command is invoked again, this time from within the post-installation.sh script. Again, it returns nothing. So you can decide that this is a clean install.Here's what happens you install when your package on Mac OS X v10.5.x:
/usr/sbin/pkgutil --pkgs=com.mycompany.pkg.mypackage
command is invoked from within the pre-installation.sh script and it returns nothing. So you can decide that this is a clean install./usr/sbin/pkgutil --pkgs=com.mycompany.pkg.mypackage
command is invoked again, this time from within the post-installation.sh script. It will return the identifier of the package. So you will consider that the installation is an upgraded. Wrong.What happens is that on Mac OS X v10.5.x, the receipts data base is upgraded after the payload has been installed. While on Mac OS X v10.6 and later, the receipts data base is upgraded at least after the post-installation script is run.
Here's a solution to this (you can probably come up with other solutions):
Tricks we use:
SHARED_INSTALLER_TEMP
env var. It exists only on Mac OS X v10.6 and later.INSTALLER_TEMP
env var that provides a random temporary folder.Once we have fixed these 2 issues, the scripts look like this:
#!/bin/sh
foundPackage=`/usr/sbin/pkgutil --volume "$3" --pkgs=$INSTALL_PKG_SESSION_ID`
if test -n "$foundPackage"; then
# It's an upgrade
/bin/sh ./preupgrade.sh
else
# It's a clean install
## For Mac OS X 10.5 ##
if [ -z "$SHARED_INSTALLER_TEMP" ]; then
/bin/echo "INSTALL" > $INSTALLER_TEMP/.install.$INSTALL_PKG_SESSION_ID
fi
## End For Mac OS X 10.5 ##
/bin/sh ./preinstall.sh
fi
exit 0
#!/bin/sh
foundPackage=`/usr/sbin/pkgutil --volume "$3" --pkgs=$INSTALL_PKG_SESSION_ID`
## For Mac OS X 10.5 ##
if [ -z "$SHARED_INSTALLER_TEMP" ]; then
if [ -f $INSTALLER_TEMP/.install.$INSTALL_PKG_SESSION_ID ]; then
foundPackage=""
/bin/rm $INSTALLER_TEMP/.install.$INSTALL_PKG_SESSION_ID
fi
fi
## End For Mac OS X 10.5 ##
if test -n "$foundPackage"; then
# It's an upgrade
/bin/sh ./postupgrade.sh
else
# It's a clean install
/bin/sh ./postinstall.sh
fi
exit 0
This solution has been successfully tested on Mac OS X 10.5.7, 10.5.8, 10.6.8, 10.7.5, 10.8.2 and 10.8.3.
Sample Project
Revision History | |
---|---|
06/26/13 | Added the --volume "$3" arguments as suggested by Paul Cook and Per Olofsson. |
Copyright 2013 Stéphane Sudre. All rights reserved.