Support for upgrade installations is a frequently requested feature. There are basically two parts to an upgrade:
When most installers refer to upgrade functionality, they refer to a), when in reality the most critical part for a successful upgrade tends to be b), which cannot be easily automated.
Upgrade installers do not create a new uninstaller. Instead, the new installed files will be appended to the existing uninstaller, and persistent variables and parameters from the upgrade installer will be as well registered in the exiting uninstaller.
Additionally, on Windows an upgrade installer will not create a new entry on the ARP (Add/Remove Programs) Menu. Instead, it will update the "version" field for the existing entry of the application. Also, it will not create a new entry into the Start Menu.
It is currently possible to create an upgrade installer by setting the <installationType> project property to "upgrade" as follows:
<project>
...
<installationType>upgrade</installationType>
...
</project>
Another approach is to switch the installer to upgrade mode at run time, using a <setInstallerVariable> action to set the "installationType" installer variable to "upgrade". This approach allows you to create a "smart" installer which starts in normal installation mode and is capable of switching to upgrade mode under certain conditions, such as detecting an existing installation.
The following example detects an existing installation by checking the existence of the ${installdir} directory: for this purpose we use the <fileTest> rule.
<project>
...
<preInstallationActionList>
<!-- detect existing installation, then switch to upgrade mode and display a note. -->
<actionGroup>
<actionList>
<showInfo>
<text>An existing installation has been detected in ${installdir}.</text>
</showInfo>
<setInstallerVariable name="allowComponentSelection" value="0" />
<setInstallerVariable name="installationType" value="upgrade" />
...
<!-- it also is possible to enable/disable components here:
<componentSelection select="customcomponentname"/>
<componentSelection deselect="customcomponentname"/>
or to perform additional actions related to the upgrade installer -->
...
</actionList>
<!-- We assume an existing installation if ${installdir} directory exists -->
<ruleList>
<fileTest condition="exists" path="${installdir}" />
</ruleList>
</actionGroup>
...
</preInstallationActionList>
...
</project>
Other approaches can be used to detect an existing installation, such as reading a Windows registry key with <registryGetKey> or checking if the value of a system environment variable (${env(PATH)}, for instance) contains a particular value: this can be done using the <compareText> rule.
By default an upgrade installer (as well as a regular installer) will overwrite existing files on disk. You can customize this global behavior by using the project property 'overwritePolicy', which can take the following values:
- 'always' : an existing file on disk will always be overwritten.
- 'never' : an exiting file on disk will never be overwritten.
- 'onlyIfNewer' : an existing file on disk will only be overwritten in case it have an older timestamp than the file to be installed.
<project>
...
<overwritePolicy>onlyIfNewer</overwritePolicy>
...
</project>
One approach to solve this could be to include the files related to upgrade installations in a separate component, which will be disabled for normal installations and enabled for upgrade installations. You can enable and disable components inside an action list using the <componentSelection> action:
<project>
...
<preInstallationActionList>
...
<!-- For an upgrade installation -->
<componentSelection>
<select>upgradecomponent</select>
<deselect>default,datacomponent</deselect>
<ruleList>
...
</ruleList>
</componentSelection>
<!-- For a normal installation -->
<componentSelection>
<select>default,datacomponent</select>
<deselect>upgradecomponent</deselect>
<ruleList>
...
</ruleList>
</componentSelection>
...
</preInstallationActionList>
...
</project>
You can make your installers check for the latest version in a specified URL. For that you will need to use the following tags in your xml project file:
<project>
...
<!-- versionId should be a positive integer number, and less than the
version number you will use in the update.xml file below described -->
<versionId></versionId>
<checkForUpdates>1</checkForUpdates>
<updateInformationURL>http://updates.yourcompany.com/update.xml</updateInformationURL>
...
</project>
The updateInformationURL points to a xml file with the update information and should match the following structure:
<installerInformation>
<versionId>2000</versionId>
<version>4.0.1</version>
<platformFileList>
<platformFile>
<filename>program-4.0.1.exe</filename>
<platform>windows</platform>
<md5></md5>
</platformFile>
<platformFile>
<filename>program-4.0.1.bin</filename>
<platform>linux</platform>
<md5></md5>
</platformFile>
</platformFileList>
<downloadLocationList>
<downloadLocation>
<url>http://updates.yourcompany.com/download/</url>
</downloadLocation>
<downloadLocation>
<url>ftp://updates.yourcompany.com/download/</url>
</downloadLocation>
</downloadLocationList>
</installerInformation>
The versionId will be compared with the current installer versionId. Finally you can specify a list with the download URL where the full download URL will be downloadLocation + filename.
On Windows, BitRock InstallBuilder automatically creates a registry entry for your program. You can use the <registryGet> action (for instance during the %b <initializationActionList>)
<registryGet>
<key>HKEY_LOCAL_MACHINE\Software\xyz\abc</key>
<name>Location</name>
<variable>installdir</variable>
<ruleList>
<platformTest type="windows" />
</ruleList>
</registryGet>
xyz:
name of your company, corresponds to the <vendor> property of the installer responsible for the installation that you want to upgrade. Defaults to "Name of your company".
abc:
product name, corresponds to the <fullName> property of the installer responsible for the installation that you want to upgrade.
installdir:
an installer variable where the resulting value of the registry key will be stored, in this case it matches the standard "installdir" parameter. If the registry key does not exist, then installdir's value will be set to empty and the value shown on screen will be taken from the installdir parameter <default> tag.
Currently we cannot detect an existing installation on Mac or Linux. This is because they lack the concept of a central registry (it could be emulated using the RPM database or writing in a central location, but it is not entirely reliable and would require administrative privileges). What we suggest in this case, is to check the default installation directory and only prompt the user for the installation directory in case the software is not found there.
<directoryParameter>
<name>installdir</name>
<description>Installation Directory</description>
<explanation></explanation>
<value></value>
<default>/path/to/default/value</default>
<allowEmptyValue>0</allowEmptyValue>
<mustBeWritable>0</mustBeWritable>
<mustExist>0</mustExist>
<width>30</width>
<ruleList>
<fileTest>
<condition>not_exists</condition>
<path>${installdir}</path>
</fileTest>
</ruleList>
</directoryParameter>